1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <getopt.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <stdint.h>
21
22#include "libfdt.h"
23
24#include "dt_table.h"
25
26
27struct dump_params {
28  const char *img_filename;
29  const char *out_filename;
30  const char *out_dtb_filename;
31};
32
33static const char short_options[] = "o:b:";
34static struct option options[] = {
35  { "output",   required_argument, NULL, 'o' },
36  { "dtb",      required_argument, NULL, 'b' },
37  { 0,          0,                 NULL, 0 }
38};
39
40
41static void *read_fdt_from_image(FILE *img_fp,
42                                 uint32_t dt_offset, uint32_t dt_size) {
43  void *fdt = NULL;
44
45  fdt = malloc(dt_size);
46
47  fseek(img_fp, dt_offset, SEEK_SET);
48  if (fread(fdt, dt_size, 1, img_fp) == 0) {
49    fprintf(stderr, "Read FDT data error.\n");
50
51    free(fdt);
52    return NULL;
53  }
54
55  return fdt;
56}
57
58static int write_fdt_to_file(const char *filename, const void *fdt) {
59  int ret = -1;
60  FILE *out_fp = NULL;
61
62  out_fp = fopen(filename, "wb");
63  if (!out_fp) {
64    fprintf(stderr, "Can not create file: %s\n", filename);
65    goto end;
66  }
67
68  size_t fdt_size = fdt_totalsize(fdt);
69  if (fwrite(fdt, fdt_size, 1, out_fp) < 1) {
70    fprintf(stderr, "Write FDT data error.\n");
71    goto end;
72  }
73
74  ret = 0;
75
76end:
77  if (out_fp) fclose(out_fp);
78
79  return ret;
80}
81
82static void free_fdt(void *fdt) {
83  if (fdt == NULL) {
84    /* do nothing */
85    return;
86  }
87
88  free(fdt);
89}
90
91
92static void output_prop_int(FILE *out_fp, const char *name, uint32_t value) {
93  fprintf(out_fp, "%+20s = %d\n", name, fdt32_to_cpu(value));
94}
95
96static void output_prop_int_cpu(FILE *out_fp, const char *name, uint32_t value) {
97  fprintf(out_fp, "%+20s = %d\n", name, value);
98}
99
100static void output_prop_hex(FILE *out_fp, const char *name, uint32_t value) {
101  fprintf(out_fp, "%+20s = %08x\n", name, fdt32_to_cpu(value));
102}
103
104static void output_prop_str(FILE *out_fp, const char *name, const char *value) {
105  fprintf(out_fp, "%+20s = %s\n", name, value);
106}
107
108static void output_table_header(FILE *out_fp, const struct dt_table_header *header) {
109  fprintf(out_fp, "dt_table_header:\n");
110  output_prop_hex(out_fp, "magic", header->magic);
111  output_prop_int(out_fp, "total_size", header->total_size);
112  output_prop_int(out_fp, "header_size", header->header_size);
113  output_prop_int(out_fp, "dt_entry_size", header->dt_entry_size);
114  output_prop_int(out_fp, "dt_entry_count", header->dt_entry_count);
115  output_prop_int(out_fp, "dt_entries_offset", header->dt_entries_offset);
116  output_prop_int(out_fp, "page_size", header->page_size);
117  output_prop_hex(out_fp, "reserved[0]", header->reserved[0]);
118}
119
120static void output_table_entry(FILE *out_fp, int index, const struct dt_table_entry *entry) {
121  fprintf(out_fp, "dt_table_entry[%d]:\n", index);
122  output_prop_int(out_fp, "dt_size", entry->dt_size);
123  output_prop_int(out_fp, "dt_offset", entry->dt_offset);
124  output_prop_hex(out_fp, "id", entry->id);
125  output_prop_hex(out_fp, "rev", entry->rev);
126  output_prop_hex(out_fp, "custom[0]", entry->custom[0]);
127  output_prop_hex(out_fp, "custom[1]", entry->custom[1]);
128  output_prop_hex(out_fp, "custom[2]", entry->custom[2]);
129  output_prop_hex(out_fp, "custom[3]", entry->custom[3]);
130}
131
132static int output_fdt_info(FILE *out_fp, void *fdt) {
133  size_t fdt_size = fdt_totalsize(fdt);
134  output_prop_int_cpu(out_fp, "(FDT)size", fdt_size);
135
136  int root_node_off = fdt_path_offset(fdt, "/");
137  if (root_node_off < 0) {
138    fprintf(stderr, "Can not get the root node.\n");
139    return -1;
140  }
141
142  const char *compatible =
143    (const char *)fdt_getprop(fdt, root_node_off, "compatible", NULL);
144  output_prop_str(out_fp, "(FDT)compatible", compatible ? compatible : "(unknown)");
145
146  return 0;
147}
148
149static int dump_image_from_fp(FILE *out_fp, FILE *img_fp,
150                              const struct dump_params *params) {
151  struct dt_table_header header;
152  if (fread(&header, sizeof(header), 1, img_fp) != 1) {
153    fprintf(stderr, "Read error.\n");
154    return -1;
155  }
156  /* TODO: check header */
157  output_table_header(out_fp, &header);
158
159  uint32_t entry_size = fdt32_to_cpu(header.dt_entry_size);
160  uint32_t entry_offset = fdt32_to_cpu(header.dt_entries_offset);
161  uint32_t entry_count = fdt32_to_cpu(header.dt_entry_count);
162  uint32_t i;
163  for (i = 0; i < entry_count; i++) {
164    struct dt_table_entry entry;
165    fseek(img_fp, entry_offset, SEEK_SET);
166    fread(&entry, sizeof(entry), 1, img_fp);
167    output_table_entry(out_fp, i, &entry);
168
169    uint32_t dt_size = fdt32_to_cpu(entry.dt_size);
170    uint32_t dt_offset = fdt32_to_cpu(entry.dt_offset);
171    if (dt_size > 0 && dt_offset > 0) {
172      void *fdt = read_fdt_from_image(img_fp, dt_offset, dt_size);
173      output_fdt_info(out_fp, fdt);
174
175      if (params->out_dtb_filename != NULL) {
176        char filename[256];
177        snprintf(filename, sizeof(filename), "%s.%d",
178                 params->out_dtb_filename, i);
179        write_fdt_to_file(filename, fdt);
180      }
181
182      free_fdt(fdt);
183    }
184
185    entry_offset += entry_size;
186  }
187
188  return 0;
189}
190
191static int process_command_dump(const struct dump_params *params) {
192  int ret = -1;
193  FILE *out_fp = NULL;
194  FILE *img_fp = NULL;
195
196  img_fp = fopen(params->img_filename, "rb");
197  if (img_fp == NULL) {
198    fprintf(stderr, "Can not open image file: %s\n", params->img_filename);
199    goto end;
200  }
201
202  if (params->out_filename != NULL) {
203    out_fp = fopen(params->out_filename, "w");
204    if (out_fp == NULL) {
205      fprintf(stderr, "Can not create file: %s\n", params->out_filename);
206      goto end;
207    }
208  }
209
210  ret = dump_image_from_fp(out_fp ? out_fp : stdout, img_fp, params);
211
212end:
213  if (img_fp) fclose(img_fp);
214  if (out_fp) fclose(out_fp);
215
216  return ret;
217}
218
219void handle_usage_dump(FILE *out_fp, const char *prog_name) {
220  fprintf(out_fp, "  %s dump <image_file> (<option>...)\n\n", prog_name);
221  fprintf(out_fp,
222    "    options:\n"
223    "      -o, --output <filename>  Output file name.\n"
224    "                               Default is output to stdout.\n"
225    "      -b, --dtb <filename>     Dump dtb/dtbo files from image.\n"
226    "                               Will output to <filename>.0, <filename>.1, etc.\n");
227}
228
229int handle_command_dump(int argc, char *argv[], int arg_start) {
230  if (argc - arg_start < 1) {
231    handle_usage_dump(stderr, argv[0]);
232    return 1;
233  }
234
235  struct dump_params params;
236  memset(&params, 0, sizeof(params));
237  params.img_filename = argv[arg_start];
238
239  optind = arg_start + 1;
240  while (1) {
241    int c = getopt_long(argc, argv, short_options, options, NULL);
242    if (c == -1) {
243      break;
244    }
245    switch (c) {
246      case 'o':
247        params.out_filename = optarg;
248        break;
249      case 'b':
250        params.out_dtb_filename = optarg;
251        break;
252      default:
253        /* Unknown option, return error */
254        return 1;
255    }
256  }
257
258  return process_command_dump(&params);
259}
260