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 <inttypes.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include "dt_table.h"
24 #include "libacpi.h"
25 #include "libfdt.h"
26
27 struct dump_params {
28 const char *img_filename;
29 const char *out_filename;
30 const char *out_dtb_filename;
31 };
32
33 static const char short_options[] = "o:b:";
34 static struct option options[] = {{"output", required_argument, NULL, 'o'},
35 {"dtb", required_argument, NULL, 'b'},
36 {0, 0, NULL, 0}};
37
read_fdt_from_image(FILE * img_fp,uint32_t dt_offset,uint32_t dt_size)38 static void *read_fdt_from_image(FILE *img_fp,
39 uint32_t dt_offset, uint32_t dt_size) {
40 void *fdt = NULL;
41
42 fdt = malloc(dt_size);
43 if (fdt == NULL) {
44 fprintf(stderr, "malloc(%" PRIu32 ") failed.\n", dt_size);
45 return NULL;
46 }
47
48 fseek(img_fp, dt_offset, SEEK_SET);
49 if (fread(fdt, dt_size, 1, img_fp) == 0) {
50 fprintf(stderr, "Read FDT data error.\n");
51
52 free(fdt);
53 return NULL;
54 }
55
56 return fdt;
57 }
58
write_fdt_to_file(const char * filename,const void * fdt,uint32_t (* get_fdt_size)(const void *))59 static int write_fdt_to_file(const char *filename, const void *fdt,
60 uint32_t (*get_fdt_size)(const void *)) {
61 int ret = -1;
62 FILE *out_fp = NULL;
63
64 out_fp = fopen(filename, "wb");
65 if (!out_fp) {
66 fprintf(stderr, "Can not create file: %s\n", filename);
67 goto end;
68 }
69
70 uint32_t fdt_size = get_fdt_size(fdt);
71 if (fwrite(fdt, fdt_size, 1, out_fp) < 1) {
72 fprintf(stderr, "Write FDT data error.\n");
73 goto end;
74 }
75
76 ret = 0;
77
78 end:
79 if (out_fp) fclose(out_fp);
80
81 return ret;
82 }
83
free_fdt(void * fdt)84 static void free_fdt(void *fdt) {
85 if (fdt == NULL) {
86 /* do nothing */
87 return;
88 }
89
90 free(fdt);
91 }
92
93
output_prop_int(FILE * out_fp,const char * name,uint32_t value)94 static void output_prop_int(FILE *out_fp, const char *name, uint32_t value) {
95 fprintf(out_fp, "%20s = %d\n", name, fdt32_to_cpu(value));
96 }
97
output_prop_int_cpu(FILE * out_fp,const char * name,uint32_t value)98 static void output_prop_int_cpu(FILE *out_fp, const char *name, uint32_t value) {
99 fprintf(out_fp, "%20s = %d\n", name, value);
100 }
101
output_prop_hex(FILE * out_fp,const char * name,uint32_t value)102 static void output_prop_hex(FILE *out_fp, const char *name, uint32_t value) {
103 fprintf(out_fp, "%20s = %08x\n", name, fdt32_to_cpu(value));
104 }
105
output_prop_str(FILE * out_fp,const char * name,const char * value)106 static void output_prop_str(FILE *out_fp, const char *name, const char *value) {
107 fprintf(out_fp, "%20s = %s\n", name, value);
108 }
109
output_table_header(FILE * out_fp,const struct dt_table_header * header)110 static void output_table_header(FILE *out_fp, const struct dt_table_header *header) {
111 fprintf(out_fp, "dt_table_header:\n");
112 output_prop_hex(out_fp, "magic", header->magic);
113 output_prop_int(out_fp, "total_size", header->total_size);
114 output_prop_int(out_fp, "header_size", header->header_size);
115 output_prop_int(out_fp, "dt_entry_size", header->dt_entry_size);
116 output_prop_int(out_fp, "dt_entry_count", header->dt_entry_count);
117 output_prop_int(out_fp, "dt_entries_offset", header->dt_entries_offset);
118 output_prop_int(out_fp, "page_size", header->page_size);
119 output_prop_int(out_fp, "version", header->version);
120 }
121
output_table_entry(FILE * out_fp,int index,const struct dt_table_entry * entry)122 static void output_table_entry(FILE *out_fp, int index, const struct dt_table_entry *entry) {
123 fprintf(out_fp, "dt_table_entry[%d]:\n", index);
124 output_prop_int(out_fp, "dt_size", entry->dt_size);
125 output_prop_int(out_fp, "dt_offset", entry->dt_offset);
126 output_prop_hex(out_fp, "id", entry->id);
127 output_prop_hex(out_fp, "rev", entry->rev);
128 output_prop_hex(out_fp, "custom[0]", entry->custom[0]);
129 output_prop_hex(out_fp, "custom[1]", entry->custom[1]);
130 output_prop_hex(out_fp, "custom[2]", entry->custom[2]);
131 output_prop_hex(out_fp, "custom[3]", entry->custom[3]);
132 }
133
output_fdt_info(FILE * out_fp,void * fdt,uint32_t (* get_fdt_size)(const void *))134 static int output_fdt_info(FILE *out_fp, void *fdt,
135 uint32_t (*get_fdt_size)(const void *)) {
136 uint32_t fdt_size = get_fdt_size(fdt);
137
138 output_prop_int_cpu(out_fp, "(FDT)size", fdt_size);
139
140 int root_node_off = fdt_path_offset(fdt, "/");
141 if (root_node_off < 0) {
142 fprintf(stderr, "Can not get the root node.\n");
143 return -1;
144 }
145
146 const char *compatible =
147 (const char *)fdt_getprop(fdt, root_node_off, "compatible", NULL);
148 output_prop_str(out_fp, "(FDT)compatible", compatible ? compatible : "(unknown)");
149
150 return 0;
151 }
152
get_acpi_file_size(const void * acpi)153 static inline uint32_t get_acpi_file_size(const void *acpi) {
154 return acpi_length(acpi);
155 }
156
get_fdt_file_size(const void * fdt)157 static inline uint32_t get_fdt_file_size(const void *fdt) {
158 return fdt_totalsize(fdt);
159 }
160
dump_image_from_fp(FILE * out_fp,FILE * img_fp,const struct dump_params * params)161 static int dump_image_from_fp(FILE *out_fp, FILE *img_fp,
162 const struct dump_params *params) {
163 struct dt_table_header header;
164 if (fread(&header, sizeof(header), 1, img_fp) != 1) {
165 fprintf(stderr, "Read error.\n");
166 return -1;
167 }
168 /* TODO: check header */
169 output_table_header(out_fp, &header);
170
171 uint32_t (*get_fdt_size)(const void *);
172 uint32_t entry_magic = fdt32_to_cpu(header.magic);
173 if (entry_magic == ACPI_TABLE_MAGIC)
174 get_fdt_size = get_acpi_file_size;
175 else
176 get_fdt_size = get_fdt_file_size;
177
178 uint32_t entry_size = fdt32_to_cpu(header.dt_entry_size);
179 uint32_t entry_offset = fdt32_to_cpu(header.dt_entries_offset);
180 uint32_t entry_count = fdt32_to_cpu(header.dt_entry_count);
181 uint32_t i;
182 for (i = 0; i < entry_count; i++) {
183 struct dt_table_entry entry;
184 fseek(img_fp, entry_offset, SEEK_SET);
185 if (fread(&entry, sizeof(entry), 1, img_fp) != 1) {
186 fprintf(stderr, "Read dt_table_entry error.\n");
187 return -1;
188 }
189 output_table_entry(out_fp, i, &entry);
190
191 uint32_t dt_size = fdt32_to_cpu(entry.dt_size);
192 uint32_t dt_offset = fdt32_to_cpu(entry.dt_offset);
193 if (dt_size > 0 && dt_offset > 0) {
194 void *fdt = read_fdt_from_image(img_fp, dt_offset, dt_size);
195 if (fdt) {
196 output_fdt_info(out_fp, fdt, get_fdt_size);
197
198 if (params->out_dtb_filename != NULL) {
199 char filename[256];
200 snprintf(filename, sizeof(filename), "%s.%d",
201 params->out_dtb_filename, i);
202 write_fdt_to_file(filename, fdt, get_fdt_size);
203 }
204
205 free_fdt(fdt);
206 }
207 }
208
209 entry_offset += entry_size;
210 }
211
212 return 0;
213 }
214
process_command_dump(const struct dump_params * params)215 static int process_command_dump(const struct dump_params *params) {
216 int ret = -1;
217 FILE *out_fp = NULL;
218 FILE *img_fp = NULL;
219
220 img_fp = fopen(params->img_filename, "rb");
221 if (img_fp == NULL) {
222 fprintf(stderr, "Can not open image file: %s\n", params->img_filename);
223 goto end;
224 }
225
226 if (params->out_filename != NULL) {
227 out_fp = fopen(params->out_filename, "w");
228 if (out_fp == NULL) {
229 fprintf(stderr, "Can not create file: %s\n", params->out_filename);
230 goto end;
231 }
232 }
233
234 ret = dump_image_from_fp(out_fp ? out_fp : stdout, img_fp, params);
235
236 end:
237 if (img_fp) fclose(img_fp);
238 if (out_fp) fclose(out_fp);
239
240 return ret;
241 }
242
handle_usage_dump(FILE * out_fp,const char * prog_name)243 void handle_usage_dump(FILE *out_fp, const char *prog_name) {
244 fprintf(out_fp, " %s dump <image_file> (<option>...)\n\n", prog_name);
245 fprintf(out_fp,
246 " options:\n"
247 " -o, --output <filename> Output file name.\n"
248 " Default is output to stdout.\n"
249 " -b, --dtb <filename> Dump dtb/dtbo files from image.\n"
250 " Will output to <filename>.0, <filename>.1, etc.\n");
251 }
252
handle_command_dump(int argc,char * argv[],int arg_start)253 int handle_command_dump(int argc, char *argv[], int arg_start) {
254 if (argc - arg_start < 1) {
255 handle_usage_dump(stderr, argv[0]);
256 return 1;
257 }
258
259 struct dump_params params;
260 memset(¶ms, 0, sizeof(params));
261 params.img_filename = argv[arg_start];
262
263 optind = arg_start + 1;
264 while (1) {
265 int c = getopt_long(argc, argv, short_options, options, NULL);
266 if (c == -1) {
267 break;
268 }
269 switch (c) {
270 case 'o':
271 params.out_filename = optarg;
272 break;
273 case 'b':
274 params.out_dtb_filename = optarg;
275 break;
276 default:
277 /* Unknown option, return error */
278 return 1;
279 }
280 }
281
282 return process_command_dump(¶ms);
283 }
284