root/scripts/dtc/fdtoverlay.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2017 Konsulko Group Inc. All rights reserved.
 *
 * Author:
 *       Pantelis Antoniou <pantelis.antoniou@konsulko.com>
 */

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

#include <libfdt.h>

#include "util.h"

#define BUF_INCREMENT   65536

/* Usage related data. */
static const char usage_synopsis[] =
        "apply a number of overlays to a base blob\n"
        "       fdtoverlay <options> [<overlay.dtbo> [<overlay.dtbo>]]";
static const char usage_short_opts[] = "i:o:v" USAGE_COMMON_SHORT_OPTS;
static struct option const usage_long_opts[] = {
        {"input",            required_argument, NULL, 'i'},
        {"output",           required_argument, NULL, 'o'},
        {"verbose",                no_argument, NULL, 'v'},
        USAGE_COMMON_LONG_OPTS,
};
static const char * const usage_opts_help[] = {
        "Input base DT blob",
        "Output DT blob",
        "Verbose messages",
        USAGE_COMMON_OPTS_HELP
};

int verbose = 0;

static void *apply_one(char *base, const char *overlay, size_t *buf_len,
                       const char *name)
{
        char *tmp = NULL;
        char *tmpo;
        int ret;
        bool has_symbols;

        /*
         * We take copies first, because a failed apply can trash
         * both the base blob and the overlay
         */
        tmpo = xmalloc(fdt_totalsize(overlay));

        do {
                tmp = xrealloc(tmp, *buf_len);
                ret = fdt_open_into(base, tmp, *buf_len);
                if (ret) {
                        fprintf(stderr,
                                "\nFailed to make temporary copy: %s\n",
                                fdt_strerror(ret));
                        goto fail;
                }
                ret = fdt_path_offset(tmp, "/__symbols__");
                has_symbols = ret >= 0;

                memcpy(tmpo, overlay, fdt_totalsize(overlay));

                ret = fdt_overlay_apply(tmp, tmpo);
                if (ret == -FDT_ERR_NOSPACE) {
                        *buf_len += BUF_INCREMENT;
                }
        } while (ret == -FDT_ERR_NOSPACE);

        if (ret) {
                fprintf(stderr, "\nFailed to apply '%s': %s\n",
                        name, fdt_strerror(ret));
                if (!has_symbols) {
                        fprintf(stderr,
                                "base blob does not have a '/__symbols__' node, "
                                "make sure you have compiled the base blob with '-@' option\n");
                }
                goto fail;
        }

        free(base);
        free(tmpo);
        return tmp;

fail:
        free(tmpo);
        if (tmp)
                free(tmp);

        return NULL;
}
static int do_fdtoverlay(const char *input_filename,
                         const char *output_filename,
                         int argc, char *argv[])
{
        char *blob = NULL;
        char **ovblob = NULL;
        size_t buf_len;
        int i, ret = -1;

        blob = utilfdt_read(input_filename, &buf_len);
        if (!blob) {
                fprintf(stderr, "\nFailed to read '%s'\n", input_filename);
                goto out_err;
        }
        if (fdt_totalsize(blob) > buf_len) {
                fprintf(stderr,
 "\nBase blob is incomplete (%lu / %" PRIu32 " bytes read)\n",
                        (unsigned long)buf_len, fdt_totalsize(blob));
                goto out_err;
        }

        /* allocate blob pointer array */
        ovblob = xmalloc(sizeof(*ovblob) * argc);
        memset(ovblob, 0, sizeof(*ovblob) * argc);

        /* read and keep track of the overlay blobs */
        for (i = 0; i < argc; i++) {
                size_t ov_len;
                ovblob[i] = utilfdt_read(argv[i], &ov_len);
                if (!ovblob[i]) {
                        fprintf(stderr, "\nFailed to read '%s'\n", argv[i]);
                        goto out_err;
                }
                if (fdt_totalsize(ovblob[i]) > ov_len) {
                        fprintf(stderr,
"\nOverlay '%s' is incomplete (%lu / %" PRIu32 " bytes read)\n",
                                argv[i], (unsigned long)ov_len,
                                fdt_totalsize(ovblob[i]));
                        goto out_err;
                }
        }

        buf_len = fdt_totalsize(blob);

        /* apply the overlays in sequence */
        for (i = 0; i < argc; i++) {
                blob = apply_one(blob, ovblob[i], &buf_len, argv[i]);
                if (!blob)
                        goto out_err;
        }

        fdt_pack(blob);
        ret = utilfdt_write(output_filename, blob);
        if (ret)
                fprintf(stderr, "\nFailed to write '%s'\n",
                        output_filename);

out_err:
        if (ovblob) {
                for (i = 0; i < argc; i++) {
                        if (ovblob[i])
                                free(ovblob[i]);
                }
                free(ovblob);
        }
        free(blob);

        return ret;
}

int main(int argc, char *argv[])
{
        int opt, i;
        char *input_filename = NULL;
        char *output_filename = NULL;

        while ((opt = util_getopt_long()) != EOF) {
                switch (opt) {
                case_USAGE_COMMON_FLAGS

                case 'i':
                        input_filename = optarg;
                        break;
                case 'o':
                        output_filename = optarg;
                        break;
                case 'v':
                        verbose = 1;
                        break;
                }
        }

        if (!input_filename)
                usage("missing input file");

        if (!output_filename)
                usage("missing output file");

        argv += optind;
        argc -= optind;

        if (argc <= 0)
                usage("missing overlay file(s)");

        if (verbose) {
                printf("input  = %s\n", input_filename);
                printf("output = %s\n", output_filename);
                for (i = 0; i < argc; i++)
                        printf("overlay[%d] = %s\n", i, argv[i]);
        }

        if (do_fdtoverlay(input_filename, output_filename, argc, argv))
                return 1;

        return 0;
}