• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
init_dt_options(struct dt_options * options)65 static void init_dt_options(struct dt_options *options) {
66   memset(options, 0, sizeof(struct dt_options));
67 }
68 
init_dt_global_options(struct dt_global_options * options)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 
copy_dt_options(struct dt_options * target,struct dt_options * options)75 static void copy_dt_options(struct dt_options *target, struct dt_options *options) {
76   memcpy(target, options, sizeof(struct dt_options));
77 }
78 
load_file_contents(FILE * fp,size_t * len_ptr)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 
load_file(const char * filename,size_t * len_ptr)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 
split_str(char ** lhs_ptr,char ** rhs_ptr,char * string,char c)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 
parse_option(char ** option_ptr,char ** value_ptr,char * line_str)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 
parse_path(char ** path_ptr,char ** prop_ptr,char * value_str)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 
get_fdt32_from_prop(void * fdt,const char * path,const char * prop)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 
get_fdt32_from_number_or_prop(void * fdt,char * value_str)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 
output_img_header(FILE * img_fp,uint32_t entry_count,uint32_t total_size,struct dt_global_options * options)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 
output_img_entry(FILE * img_fp,size_t entry_offset,struct dt_image_writer_fdt_info * fdt_info,struct dt_options * options,int output_fdt)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 
dt_image_writer_start(FILE * img_fp,uint32_t entry_count)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 
set_dt_options(struct dt_options * options,const char * option,const char * value)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 
set_global_options(struct dt_image_writer * writer,const char * option,const char * value)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 
set_entry_options(struct dt_image_writer * writer,const char * option,const char * value)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 
search_fdt_info(struct dt_image_writer * writer,const char * filename)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 
add_fdt_info(struct dt_image_writer * writer,const char * filename,uint32_t dt_offset)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 
flush_entry_to_img(struct dt_image_writer * writer)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 
dt_image_writer_add_entry(struct dt_image_writer * writer,const char * fdt_filename)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 
dt_image_writer_end(struct dt_image_writer * writer)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