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(&params, 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(&params);
283 }
284