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