linux/scripts/dtc/fdtput.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
   4 */
   5
   6#include <assert.h>
   7#include <ctype.h>
   8#include <getopt.h>
   9#include <stdio.h>
  10#include <stdlib.h>
  11#include <string.h>
  12
  13#include <libfdt.h>
  14
  15#include "util.h"
  16
  17/* These are the operations we support */
  18enum oper_type {
  19        OPER_WRITE_PROP,                /* Write a property in a node */
  20        OPER_CREATE_NODE,               /* Create a new node */
  21};
  22
  23struct display_info {
  24        enum oper_type oper;    /* operation to perform */
  25        int type;               /* data type (s/i/u/x or 0 for default) */
  26        int size;               /* data size (1/2/4) */
  27        int verbose;            /* verbose output */
  28        int auto_path;          /* automatically create all path components */
  29};
  30
  31
  32/**
  33 * Report an error with a particular node.
  34 *
  35 * @param name          Node name to report error on
  36 * @param namelen       Length of node name, or -1 to use entire string
  37 * @param err           Error number to report (-FDT_ERR_...)
  38 */
  39static void report_error(const char *name, int namelen, int err)
  40{
  41        if (namelen == -1)
  42                namelen = strlen(name);
  43        fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
  44                fdt_strerror(err));
  45}
  46
  47/**
  48 * Encode a series of arguments in a property value.
  49 *
  50 * @param disp          Display information / options
  51 * @param arg           List of arguments from command line
  52 * @param arg_count     Number of arguments (may be 0)
  53 * @param valuep        Returns buffer containing value
  54 * @param *value_len    Returns length of value encoded
  55 */
  56static int encode_value(struct display_info *disp, char **arg, int arg_count,
  57                        char **valuep, int *value_len)
  58{
  59        char *value = NULL;     /* holding area for value */
  60        int value_size = 0;     /* size of holding area */
  61        char *ptr;              /* pointer to current value position */
  62        int len;                /* length of this cell/string/byte */
  63        int ival;
  64        int upto;       /* the number of bytes we have written to buf */
  65        char fmt[3];
  66
  67        upto = 0;
  68
  69        if (disp->verbose)
  70                fprintf(stderr, "Decoding value:\n");
  71
  72        fmt[0] = '%';
  73        fmt[1] = disp->type ? disp->type : 'd';
  74        fmt[2] = '\0';
  75        for (; arg_count > 0; arg++, arg_count--, upto += len) {
  76                /* assume integer unless told otherwise */
  77                if (disp->type == 's')
  78                        len = strlen(*arg) + 1;
  79                else
  80                        len = disp->size == -1 ? 4 : disp->size;
  81
  82                /* enlarge our value buffer by a suitable margin if needed */
  83                if (upto + len > value_size) {
  84                        value_size = (upto + len) + 500;
  85                        value = realloc(value, value_size);
  86                        if (!value) {
  87                                fprintf(stderr, "Out of mmory: cannot alloc "
  88                                        "%d bytes\n", value_size);
  89                                return -1;
  90                        }
  91                }
  92
  93                ptr = value + upto;
  94                if (disp->type == 's') {
  95                        memcpy(ptr, *arg, len);
  96                        if (disp->verbose)
  97                                fprintf(stderr, "\tstring: '%s'\n", ptr);
  98                } else {
  99                        int *iptr = (int *)ptr;
 100                        sscanf(*arg, fmt, &ival);
 101                        if (len == 4)
 102                                *iptr = cpu_to_fdt32(ival);
 103                        else
 104                                *ptr = (uint8_t)ival;
 105                        if (disp->verbose) {
 106                                fprintf(stderr, "\t%s: %d\n",
 107                                        disp->size == 1 ? "byte" :
 108                                        disp->size == 2 ? "short" : "int",
 109                                        ival);
 110                        }
 111                }
 112        }
 113        *value_len = upto;
 114        *valuep = value;
 115        if (disp->verbose)
 116                fprintf(stderr, "Value size %d\n", upto);
 117        return 0;
 118}
 119
 120static int store_key_value(void *blob, const char *node_name,
 121                const char *property, const char *buf, int len)
 122{
 123        int node;
 124        int err;
 125
 126        node = fdt_path_offset(blob, node_name);
 127        if (node < 0) {
 128                report_error(node_name, -1, node);
 129                return -1;
 130        }
 131
 132        err = fdt_setprop(blob, node, property, buf, len);
 133        if (err) {
 134                report_error(property, -1, err);
 135                return -1;
 136        }
 137        return 0;
 138}
 139
 140/**
 141 * Create paths as needed for all components of a path
 142 *
 143 * Any components of the path that do not exist are created. Errors are
 144 * reported.
 145 *
 146 * @param blob          FDT blob to write into
 147 * @param in_path       Path to process
 148 * @return 0 if ok, -1 on error
 149 */
 150static int create_paths(void *blob, const char *in_path)
 151{
 152        const char *path = in_path;
 153        const char *sep;
 154        int node, offset = 0;
 155
 156        /* skip leading '/' */
 157        while (*path == '/')
 158                path++;
 159
 160        for (sep = path; *sep; path = sep + 1, offset = node) {
 161                /* equivalent to strchrnul(), but it requires _GNU_SOURCE */
 162                sep = strchr(path, '/');
 163                if (!sep)
 164                        sep = path + strlen(path);
 165
 166                node = fdt_subnode_offset_namelen(blob, offset, path,
 167                                sep - path);
 168                if (node == -FDT_ERR_NOTFOUND) {
 169                        node = fdt_add_subnode_namelen(blob, offset, path,
 170                                                       sep - path);
 171                }
 172                if (node < 0) {
 173                        report_error(path, sep - path, node);
 174                        return -1;
 175                }
 176        }
 177
 178        return 0;
 179}
 180
 181/**
 182 * Create a new node in the fdt.
 183 *
 184 * This will overwrite the node_name string. Any error is reported.
 185 *
 186 * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
 187 *
 188 * @param blob          FDT blob to write into
 189 * @param node_name     Name of node to create
 190 * @return new node offset if found, or -1 on failure
 191 */
 192static int create_node(void *blob, const char *node_name)
 193{
 194        int node = 0;
 195        char *p;
 196
 197        p = strrchr(node_name, '/');
 198        if (!p) {
 199                report_error(node_name, -1, -FDT_ERR_BADPATH);
 200                return -1;
 201        }
 202        *p = '\0';
 203
 204        if (p > node_name) {
 205                node = fdt_path_offset(blob, node_name);
 206                if (node < 0) {
 207                        report_error(node_name, -1, node);
 208                        return -1;
 209                }
 210        }
 211
 212        node = fdt_add_subnode(blob, node, p + 1);
 213        if (node < 0) {
 214                report_error(p + 1, -1, node);
 215                return -1;
 216        }
 217
 218        return 0;
 219}
 220
 221static int do_fdtput(struct display_info *disp, const char *filename,
 222                    char **arg, int arg_count)
 223{
 224        char *value;
 225        char *blob;
 226        int len, ret = 0;
 227
 228        blob = utilfdt_read(filename);
 229        if (!blob)
 230                return -1;
 231
 232        switch (disp->oper) {
 233        case OPER_WRITE_PROP:
 234                /*
 235                 * Convert the arguments into a single binary value, then
 236                 * store them into the property.
 237                 */
 238                assert(arg_count >= 2);
 239                if (disp->auto_path && create_paths(blob, *arg))
 240                        return -1;
 241                if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
 242                        store_key_value(blob, *arg, arg[1], value, len))
 243                        ret = -1;
 244                break;
 245        case OPER_CREATE_NODE:
 246                for (; ret >= 0 && arg_count--; arg++) {
 247                        if (disp->auto_path)
 248                                ret = create_paths(blob, *arg);
 249                        else
 250                                ret = create_node(blob, *arg);
 251                }
 252                break;
 253        }
 254        if (ret >= 0)
 255                ret = utilfdt_write(filename, blob);
 256
 257        free(blob);
 258        return ret;
 259}
 260
 261static const char *usage_msg =
 262        "fdtput - write a property value to a device tree\n"
 263        "\n"
 264        "The command line arguments are joined together into a single value.\n"
 265        "\n"
 266        "Usage:\n"
 267        "       fdtput <options> <dt file> <node> <property> [<value>...]\n"
 268        "       fdtput -c <options> <dt file> [<node>...]\n"
 269        "Options:\n"
 270        "\t-c\t\tCreate nodes if they don't already exist\n"
 271        "\t-p\t\tAutomatically create nodes as needed for the node path\n"
 272        "\t-t <type>\tType of data\n"
 273        "\t-v\t\tVerbose: display each value decoded from command line\n"
 274        "\t-h\t\tPrint this help\n\n"
 275        USAGE_TYPE_MSG;
 276
 277static void usage(const char *msg)
 278{
 279        if (msg)
 280                fprintf(stderr, "Error: %s\n\n", msg);
 281
 282        fprintf(stderr, "%s", usage_msg);
 283        exit(2);
 284}
 285
 286int main(int argc, char *argv[])
 287{
 288        struct display_info disp;
 289        char *filename = NULL;
 290
 291        memset(&disp, '\0', sizeof(disp));
 292        disp.size = -1;
 293        disp.oper = OPER_WRITE_PROP;
 294        for (;;) {
 295                int c = getopt(argc, argv, "chpt:v");
 296                if (c == -1)
 297                        break;
 298
 299                /*
 300                 * TODO: add options to:
 301                 * - delete property
 302                 * - delete node (optionally recursively)
 303                 * - rename node
 304                 * - pack fdt before writing
 305                 * - set amount of free space when writing
 306                 * - expand fdt if value doesn't fit
 307                 */
 308                switch (c) {
 309                case 'c':
 310                        disp.oper = OPER_CREATE_NODE;
 311                        break;
 312                case 'h':
 313                case '?':
 314                        usage(NULL);
 315                case 'p':
 316                        disp.auto_path = 1;
 317                        break;
 318                case 't':
 319                        if (utilfdt_decode_type(optarg, &disp.type,
 320                                        &disp.size))
 321                                usage("Invalid type string");
 322                        break;
 323
 324                case 'v':
 325                        disp.verbose = 1;
 326                        break;
 327                }
 328        }
 329
 330        if (optind < argc)
 331                filename = argv[optind++];
 332        if (!filename)
 333                usage("Missing filename");
 334
 335        argv += optind;
 336        argc -= optind;
 337
 338        if (disp.oper == OPER_WRITE_PROP) {
 339                if (argc < 1)
 340                        usage("Missing node");
 341                if (argc < 2)
 342                        usage("Missing property");
 343        }
 344
 345        if (do_fdtput(&disp, filename, argv, argc))
 346                return 1;
 347        return 0;
 348}
 349