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