mkdtimg_core.c revision 6ad4caae73191bdc2403185b6482969c17604c95
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 {
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
59static void init_dt_options(struct dt_options *options) {
60  memset(options, 0, sizeof(struct dt_options));
61}
62
63static 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
68static void copy_dt_options(struct dt_options *target, struct dt_options *options) {
69  memcpy(target, options, sizeof(struct dt_options));
70}
71
72static 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
95static 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
108static 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
122int parse_option(char **option_ptr, char **value_ptr, char *line_str) {
123  return split_str(option_ptr, value_ptr, line_str, '=');
124}
125
126int parse_path(char **path_ptr, char **prop_ptr, char *value_str) {
127  return split_str(path_ptr, prop_ptr, value_str, ':');
128}
129
130static 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 = fdt32_to_cpu(*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
151static 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
168static 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
183static 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
229end:
230  if (fdt) free(fdt);
231
232  return ret;
233}
234
235
236struct 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
254error:
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
263static 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
284int 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
297int 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
302static 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
340int 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
359int 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
377end:
378  free(writer->past_filenames);
379  free(writer->past_dt_offsets);
380  free(writer);
381
382  return ret;
383}
384