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