| // 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; | |
| } |