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 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 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 81 static void free_fdt(void *fdt) { 82 if (fdt == NULL) { 83 /* do nothing */ 84 return; 85 } 86 87 free(fdt); 88 } 89 90 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 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 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 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 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 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 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 150 static inline uint32_t get_acpi_file_size(const void *acpi) { 151 return acpi_length(acpi); 152 } 153 154 static inline uint32_t get_fdt_file_size(const void *fdt) { 155 return fdt_totalsize(fdt); 156 } 157 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 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 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 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(¶ms, 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(¶ms); 278 } 279