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