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 "mkdtimg_core.h" 18 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <stdint.h> 23 #include <unistd.h> 24 25 #include "libacpi.h" 26 #include "libfdt.h" 27 28 #include "dt_table.h" 29 30 #define DEBUG 0 31 32 33 struct dt_options { 34 char id[OPTION_VALUE_SIZE_MAX]; 35 char rev[OPTION_VALUE_SIZE_MAX]; 36 char custom[4][OPTION_VALUE_SIZE_MAX]; 37 }; 38 39 struct dt_global_options { 40 struct dt_options default_options; 41 enum DT_TYPE dt_type; 42 uint32_t page_size; 43 uint32_t version; 44 }; 45 46 struct dt_image_writer_fdt_info { 47 char filename[1024]; 48 uint32_t dt_offset; 49 }; 50 51 struct dt_image_writer { 52 FILE *img_fp; 53 54 struct dt_global_options global_options; 55 struct dt_options entry_options; 56 57 char entry_filename[1024]; 58 uint32_t entry_count; 59 uint32_t entry_offset; 60 uint32_t dt_offset; 61 62 struct dt_image_writer_fdt_info *fdt_infos; 63 uint32_t fdt_info_count; 64 }; 65 66 67 static void init_dt_options(struct dt_options *options) { 68 memset(options, 0, sizeof(struct dt_options)); 69 } 70 71 static void init_dt_global_options(struct dt_global_options *options) { 72 init_dt_options(&options->default_options); 73 options->dt_type = DTB; 74 options->page_size = DT_TABLE_DEFAULT_PAGE_SIZE; 75 options->version = DT_TABLE_DEFAULT_VERSION; 76 } 77 78 static void copy_dt_options(struct dt_options *target, struct dt_options *options) { 79 memcpy(target, options, sizeof(struct dt_options)); 80 } 81 82 static char *load_file_contents(FILE *fp, size_t *len_ptr) { 83 // Gets the file size. 84 fseek(fp, 0, SEEK_END); 85 size_t len = ftell(fp); 86 fseek(fp, 0, SEEK_SET); 87 88 char *buf = malloc(len); 89 if (buf == NULL) { 90 return NULL; 91 } 92 93 if (fread(buf, len, 1, fp) != 1) { 94 free(buf); 95 return NULL; 96 } 97 98 if (len_ptr) { 99 *len_ptr = len; 100 } 101 102 return buf; 103 } 104 105 static char *load_file(const char *filename, size_t *len_ptr) { 106 FILE *fp = fopen(filename, "r"); 107 if (!fp) { 108 return NULL; 109 } 110 111 char *buf = load_file_contents(fp, len_ptr); 112 113 fclose(fp); 114 115 return buf; 116 } 117 118 static int split_str(char **lhs_ptr, char **rhs_ptr, char *string, char c) { 119 char *middle_ptr = strchr(string, c); 120 if (middle_ptr == NULL) { 121 return -1; 122 } 123 124 *middle_ptr = '\0'; 125 126 *lhs_ptr = string; 127 *rhs_ptr = middle_ptr + 1; 128 129 return 0; 130 } 131 132 int parse_option(char **option_ptr, char **value_ptr, char *line_str) { 133 return split_str(option_ptr, value_ptr, line_str, '='); 134 } 135 136 int parse_path(char **path_ptr, char **prop_ptr, char *value_str) { 137 return split_str(path_ptr, prop_ptr, value_str, ':'); 138 } 139 140 static fdt32_t get_fdt32_from_prop(void *fdt, const char *path, const char *prop) { 141 int node_off = fdt_path_offset(fdt, path); 142 if (node_off < 0) { 143 fprintf(stderr, "Can not find node: %s\n", path); 144 return 0; 145 } 146 147 int len; 148 fdt32_t *prop_value_ptr = (fdt32_t *)fdt_getprop(fdt, node_off, prop, &len); 149 if (prop_value_ptr == NULL) { 150 fprintf(stderr, "Can not find property: %s:%s\n", path, prop); 151 return 0; 152 } 153 154 fdt32_t value = *prop_value_ptr; 155 /* TODO: check len */ 156 if (DEBUG) printf("%s:%s => %08x\n", path, prop, fdt32_to_cpu(value)); 157 158 return value; 159 } 160 161 static fdt32_t get_fdt32_from_number_or_prop(void *fdt, char *value_str) { 162 if (value_str[0] == '/') { 163 char *path, *prop; 164 if (parse_path(&path, &prop, value_str) != 0) { 165 fprintf(stderr, "Wrong syntax: %s\n", value_str); 166 return 0; 167 } 168 return get_fdt32_from_prop(fdt, path, prop); 169 } 170 171 /* It should be a number */ 172 char *end; 173 uint32_t value = strtoul(value_str, &end, 0); 174 /* TODO: check end */ 175 return cpu_to_fdt32(value); 176 } 177 178 static int output_img_header(FILE *img_fp, 179 uint32_t entry_count, uint32_t total_size, 180 struct dt_global_options *options) { 181 struct dt_table_header header; 182 dt_table_header_init(&header, options->dt_type); 183 184 header.dt_entry_count = cpu_to_fdt32(entry_count); 185 header.total_size = cpu_to_fdt32(total_size); 186 header.page_size = cpu_to_fdt32(options->page_size); 187 header.version = cpu_to_fdt32(options->version); 188 189 fseek(img_fp, 0, SEEK_SET); 190 fwrite(&header, sizeof(header), 1, img_fp); 191 192 return 0; 193 } 194 195 static int32_t output_img_entry(FILE *img_fp, size_t entry_offset, 196 int (*fdt_verifier)(void *, size_t), 197 struct dt_image_writer_fdt_info *fdt_info, 198 struct dt_options *options, int output_fdt) { 199 int32_t ret = -1; 200 void *fdt = NULL; 201 202 size_t fdt_file_size; 203 fdt = load_file(fdt_info->filename, &fdt_file_size); 204 if (fdt == NULL) { 205 fprintf(stderr, "Can not read file: %s\n", fdt_info->filename); 206 goto end; 207 } 208 209 ret = fdt_verifier(fdt, fdt_file_size); 210 if (ret < 0) { 211 fprintf(stderr, "File '%s' verification failed.\n", fdt_info->filename); 212 goto end; 213 } 214 215 /* Prepare dt_table_entry and output */ 216 struct dt_table_entry entry; 217 entry.dt_size = cpu_to_fdt32(fdt_file_size); 218 entry.dt_offset = cpu_to_fdt32(fdt_info->dt_offset); 219 entry.id = get_fdt32_from_number_or_prop(fdt, options->id); 220 entry.rev = get_fdt32_from_number_or_prop(fdt, options->rev); 221 entry.custom[0] = get_fdt32_from_number_or_prop(fdt, options->custom[0]); 222 entry.custom[1] = get_fdt32_from_number_or_prop(fdt, options->custom[1]); 223 entry.custom[2] = get_fdt32_from_number_or_prop(fdt, options->custom[2]); 224 entry.custom[3] = get_fdt32_from_number_or_prop(fdt, options->custom[3]); 225 fseek(img_fp, entry_offset, SEEK_SET); 226 fwrite(&entry, sizeof(entry), 1, img_fp); 227 228 if (output_fdt) { 229 fseek(img_fp, fdt_info->dt_offset, SEEK_SET); 230 fwrite(fdt, fdt_file_size, 1, img_fp); 231 ret = fdt_file_size; 232 } else { 233 ret = 0; 234 } 235 236 end: 237 if (fdt) free(fdt); 238 239 return ret; 240 } 241 242 struct dt_image_writer *dt_image_writer_start(FILE *img_fp, uint32_t entry_count) { 243 struct dt_image_writer *writer = NULL; 244 struct dt_image_writer_fdt_info *fdt_infos = NULL; 245 246 writer = malloc(sizeof(struct dt_image_writer)); 247 if (!writer) goto error; 248 249 fdt_infos = malloc(sizeof(struct dt_image_writer_fdt_info) * entry_count); 250 if (!fdt_infos) goto error; 251 252 writer->img_fp = img_fp; 253 init_dt_global_options(&writer->global_options); 254 init_dt_options(&writer->entry_options); 255 writer->entry_filename[0] = '\0'; 256 writer->entry_count = entry_count; 257 writer->entry_offset = sizeof(struct dt_table_header); 258 writer->dt_offset = 259 writer->entry_offset + sizeof(struct dt_table_entry) * entry_count; 260 writer->fdt_infos = fdt_infos; 261 writer->fdt_info_count = 0; 262 263 return writer; 264 265 error: 266 fprintf(stderr, "Unable to start writer\n"); 267 268 if (fdt_infos) free(fdt_infos); 269 if (writer) free(writer); 270 271 return NULL; 272 } 273 274 static int set_dt_options(struct dt_options *options, 275 const char *option, const char *value) { 276 if (strcmp(option, "id") == 0) { 277 strncpy(options->id, value, OPTION_VALUE_SIZE_MAX - 1); 278 } else if (strcmp(option, "rev") == 0) { 279 strncpy(options->rev, value, OPTION_VALUE_SIZE_MAX - 1); 280 } else if (strcmp(option, "custom0") == 0) { 281 strncpy(options->custom[0], value, OPTION_VALUE_SIZE_MAX - 1); 282 } else if (strcmp(option, "custom1") == 0) { 283 strncpy(options->custom[1], value, OPTION_VALUE_SIZE_MAX - 1); 284 } else if (strcmp(option, "custom2") == 0) { 285 strncpy(options->custom[2], value, OPTION_VALUE_SIZE_MAX - 1); 286 } else if (strcmp(option, "custom3") == 0) { 287 strncpy(options->custom[3], value, OPTION_VALUE_SIZE_MAX - 1); 288 } else { 289 return -1; 290 } 291 292 return 0; 293 } 294 295 int set_global_options(struct dt_image_writer *writer, 296 const char *option, const char *value) { 297 struct dt_global_options *global_options = &writer->global_options; 298 299 if (strcmp(option, "page_size") == 0) { 300 global_options->page_size = strtoul(value, NULL, 0); 301 } else if (strcmp(option, "version") == 0) { 302 global_options->version = strtoul(value, NULL, 0); 303 } else if (strcmp(option, "dt_type") == 0) { 304 if (!strcmp(value, "acpi")) { 305 global_options->dt_type = ACPI; 306 } else { 307 global_options->dt_type = DTB; 308 } 309 } else { 310 return set_dt_options(&global_options->default_options, option, value); 311 } 312 313 return 0; 314 } 315 316 int set_entry_options(struct dt_image_writer *writer, 317 const char *option, const char *value) { 318 return set_dt_options(&writer->entry_options, option, value); 319 } 320 321 static struct dt_image_writer_fdt_info *search_fdt_info( 322 struct dt_image_writer *writer, const char *filename) { 323 for (uint32_t i = 0; i < writer->fdt_info_count; i++) { 324 struct dt_image_writer_fdt_info *fdt_info = &writer->fdt_infos[i]; 325 if (strcmp(fdt_info->filename, filename) == 0) { 326 return fdt_info; 327 } 328 } 329 return NULL; 330 } 331 332 static struct dt_image_writer_fdt_info *add_fdt_info( 333 struct dt_image_writer *writer, const char *filename, uint32_t dt_offset) { 334 struct dt_image_writer_fdt_info *fdt_info = 335 &writer->fdt_infos[writer->fdt_info_count]; 336 337 strncpy(fdt_info->filename, filename, sizeof(fdt_info->filename) - 1); 338 fdt_info->dt_offset = dt_offset; 339 340 writer->fdt_info_count++; 341 342 return fdt_info; 343 } 344 345 static int acpi_file_verifier(void *acpi, size_t acpi_file_size) { 346 size_t acpi_size; 347 acpi_size = acpi_length(acpi); 348 if (acpi_size != acpi_file_size) { 349 fprintf(stderr, "The file size and ACPI/ACPIO size are not matched.\n"); 350 return -1; 351 } 352 353 if (acpi_csum(acpi, acpi_size)) { 354 fprintf(stderr, "ACPI/ACPIO CRC checksum failed.\n"); 355 return -1; 356 } 357 358 return 0; 359 } 360 361 static int fdt_file_verifier(void *fdt, size_t fdt_file_size) { 362 if (fdt_check_header(fdt) != 0) { 363 fprintf(stderr, "Bad FDT header.\n"); 364 return -1; 365 } 366 367 size_t fdt_size; 368 fdt_size = fdt_totalsize(fdt); 369 if (fdt_size != fdt_file_size) { 370 fprintf(stderr, "The file size and FDT size are not matched.\n"); 371 return -1; 372 } 373 374 return 0; 375 } 376 377 static int flush_entry_to_img(struct dt_image_writer *writer) { 378 if (writer->entry_filename[0] == '\0') { 379 return 0; 380 } 381 382 struct dt_image_writer_fdt_info *fdt_info = 383 search_fdt_info(writer, writer->entry_filename); 384 int output_fdt = (fdt_info == NULL); 385 if (fdt_info == NULL) { 386 fdt_info = add_fdt_info(writer, writer->entry_filename, writer->dt_offset); 387 } 388 389 int (*fdt_verifier)(void *fdt, size_t fdt_file_size); 390 if (writer->global_options.dt_type == ACPI) { 391 fdt_verifier = acpi_file_verifier; 392 } else { 393 fdt_verifier = fdt_file_verifier; 394 } 395 396 int32_t dt_size = 397 output_img_entry(writer->img_fp, writer->entry_offset, fdt_verifier, 398 fdt_info, &writer->entry_options, output_fdt); 399 if (dt_size == -1) return -1; 400 401 writer->entry_offset += sizeof(struct dt_table_entry); 402 writer->dt_offset += dt_size; 403 404 return 0; 405 } 406 407 int dt_image_writer_add_entry(struct dt_image_writer *writer, 408 const char *fdt_filename) { 409 if (flush_entry_to_img(writer) != 0) { 410 return -1; 411 } 412 413 strncpy( 414 writer->entry_filename, 415 fdt_filename, 416 sizeof(writer->entry_filename) - 1); 417 418 /* Copy the default_options as default */ 419 copy_dt_options( 420 &writer->entry_options, 421 &writer->global_options.default_options); 422 423 return 0; 424 } 425 426 int dt_image_writer_end(struct dt_image_writer *writer) { 427 int ret = -1; 428 429 if (flush_entry_to_img(writer) != 0) { 430 goto end; 431 } 432 433 if (output_img_header( 434 writer->img_fp, 435 writer->entry_count, 436 writer->dt_offset, 437 &writer->global_options) != 0) { 438 goto end; 439 } 440 441 printf("Total %d entries.\n", writer->entry_count); 442 ret = 0; 443 444 end: 445 free(writer->fdt_infos); 446 free(writer); 447 448 return ret; 449 } 450