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