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