/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <getopt.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include "libfdt.h" #include "dt_table.h" struct dump_params { const char *img_filename; const char *out_filename; const char *out_dtb_filename; }; static const char short_options[] = "o:b:"; static struct option options[] = { { "output", required_argument, NULL, 'o' }, { "dtb", required_argument, NULL, 'b' }, { 0, 0, NULL, 0 } }; static void *read_fdt_from_image(FILE *img_fp, uint32_t dt_offset, uint32_t dt_size) { void *fdt = NULL; fdt = malloc(dt_size); fseek(img_fp, dt_offset, SEEK_SET); if (fread(fdt, dt_size, 1, img_fp) == 0) { fprintf(stderr, "Read FDT data error.\n"); free(fdt); return NULL; } return fdt; } static int write_fdt_to_file(const char *filename, const void *fdt) { int ret = -1; FILE *out_fp = NULL; out_fp = fopen(filename, "wb"); if (!out_fp) { fprintf(stderr, "Can not create file: %s\n", filename); goto end; } size_t fdt_size = fdt_totalsize(fdt); if (fwrite(fdt, fdt_size, 1, out_fp) < 1) { fprintf(stderr, "Write FDT data error.\n"); goto end; } ret = 0; end: if (out_fp) fclose(out_fp); return ret; } static void free_fdt(void *fdt) { if (fdt == NULL) { /* do nothing */ return; } free(fdt); } static void output_prop_int(FILE *out_fp, const char *name, uint32_t value) { fprintf(out_fp, "%+20s = %d\n", name, fdt32_to_cpu(value)); } static void output_prop_int_cpu(FILE *out_fp, const char *name, uint32_t value) { fprintf(out_fp, "%+20s = %d\n", name, value); } static void output_prop_hex(FILE *out_fp, const char *name, uint32_t value) { fprintf(out_fp, "%+20s = %08x\n", name, fdt32_to_cpu(value)); } static void output_prop_str(FILE *out_fp, const char *name, const char *value) { fprintf(out_fp, "%+20s = %s\n", name, value); } static void output_table_header(FILE *out_fp, const struct dt_table_header *header) { fprintf(out_fp, "dt_table_header:\n"); output_prop_hex(out_fp, "magic", header->magic); output_prop_int(out_fp, "total_size", header->total_size); output_prop_int(out_fp, "header_size", header->header_size); output_prop_int(out_fp, "dt_entry_size", header->dt_entry_size); output_prop_int(out_fp, "dt_entry_count", header->dt_entry_count); output_prop_int(out_fp, "dt_entries_offset", header->dt_entries_offset); output_prop_int(out_fp, "page_size", header->page_size); output_prop_int(out_fp, "version", header->version); } static void output_table_entry(FILE *out_fp, int index, const struct dt_table_entry *entry) { fprintf(out_fp, "dt_table_entry[%d]:\n", index); output_prop_int(out_fp, "dt_size", entry->dt_size); output_prop_int(out_fp, "dt_offset", entry->dt_offset); output_prop_hex(out_fp, "id", entry->id); output_prop_hex(out_fp, "rev", entry->rev); output_prop_hex(out_fp, "custom[0]", entry->custom[0]); output_prop_hex(out_fp, "custom[1]", entry->custom[1]); output_prop_hex(out_fp, "custom[2]", entry->custom[2]); output_prop_hex(out_fp, "custom[3]", entry->custom[3]); } static int output_fdt_info(FILE *out_fp, void *fdt) { size_t fdt_size = fdt_totalsize(fdt); output_prop_int_cpu(out_fp, "(FDT)size", fdt_size); int root_node_off = fdt_path_offset(fdt, "/"); if (root_node_off < 0) { fprintf(stderr, "Can not get the root node.\n"); return -1; } const char *compatible = (const char *)fdt_getprop(fdt, root_node_off, "compatible", NULL); output_prop_str(out_fp, "(FDT)compatible", compatible ? compatible : "(unknown)"); return 0; } static int dump_image_from_fp(FILE *out_fp, FILE *img_fp, const struct dump_params *params) { struct dt_table_header header; if (fread(&header, sizeof(header), 1, img_fp) != 1) { fprintf(stderr, "Read error.\n"); return -1; } /* TODO: check header */ output_table_header(out_fp, &header); uint32_t entry_size = fdt32_to_cpu(header.dt_entry_size); uint32_t entry_offset = fdt32_to_cpu(header.dt_entries_offset); uint32_t entry_count = fdt32_to_cpu(header.dt_entry_count); uint32_t i; for (i = 0; i < entry_count; i++) { struct dt_table_entry entry; fseek(img_fp, entry_offset, SEEK_SET); if (fread(&entry, sizeof(entry), 1, img_fp) != 1) { fprintf(stderr, "Read dt_table_entry error.\n"); return -1; } output_table_entry(out_fp, i, &entry); uint32_t dt_size = fdt32_to_cpu(entry.dt_size); uint32_t dt_offset = fdt32_to_cpu(entry.dt_offset); if (dt_size > 0 && dt_offset > 0) { void *fdt = read_fdt_from_image(img_fp, dt_offset, dt_size); output_fdt_info(out_fp, fdt); if (params->out_dtb_filename != NULL) { char filename[256]; snprintf(filename, sizeof(filename), "%s.%d", params->out_dtb_filename, i); write_fdt_to_file(filename, fdt); } free_fdt(fdt); } entry_offset += entry_size; } return 0; } static int process_command_dump(const struct dump_params *params) { int ret = -1; FILE *out_fp = NULL; FILE *img_fp = NULL; img_fp = fopen(params->img_filename, "rb"); if (img_fp == NULL) { fprintf(stderr, "Can not open image file: %s\n", params->img_filename); goto end; } if (params->out_filename != NULL) { out_fp = fopen(params->out_filename, "w"); if (out_fp == NULL) { fprintf(stderr, "Can not create file: %s\n", params->out_filename); goto end; } } ret = dump_image_from_fp(out_fp ? out_fp : stdout, img_fp, params); end: if (img_fp) fclose(img_fp); if (out_fp) fclose(out_fp); return ret; } void handle_usage_dump(FILE *out_fp, const char *prog_name) { fprintf(out_fp, " %s dump <image_file> (<option>...)\n\n", prog_name); fprintf(out_fp, " options:\n" " -o, --output <filename> Output file name.\n" " Default is output to stdout.\n" " -b, --dtb <filename> Dump dtb/dtbo files from image.\n" " Will output to <filename>.0, <filename>.1, etc.\n"); } int handle_command_dump(int argc, char *argv[], int arg_start) { if (argc - arg_start < 1) { handle_usage_dump(stderr, argv[0]); return 1; } struct dump_params params; memset(¶ms, 0, sizeof(params)); params.img_filename = argv[arg_start]; optind = arg_start + 1; while (1) { int c = getopt_long(argc, argv, short_options, options, NULL); if (c == -1) { break; } switch (c) { case 'o': params.out_filename = optarg; break; case 'b': params.out_dtb_filename = optarg; break; default: /* Unknown option, return error */ return 1; } } return process_command_dump(¶ms); }