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 };
42
43 struct dt_image_writer_fdt_info {
44 char filename[1024];
45 uint32_t dt_offset;
46 };
47
48 struct 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
init_dt_options(struct dt_options * options)64 static void init_dt_options(struct dt_options *options) {
65 memset(options, 0, sizeof(struct dt_options));
66 }
67
init_dt_global_options(struct dt_global_options * options)68 static 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
copy_dt_options(struct dt_options * target,struct dt_options * options)73 static void copy_dt_options(struct dt_options *target, struct dt_options *options) {
74 memcpy(target, options, sizeof(struct dt_options));
75 }
76
load_file_contents(FILE * fp,size_t * len_ptr)77 static 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
load_file(const char * filename,size_t * len_ptr)100 static 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
split_str(char ** lhs_ptr,char ** rhs_ptr,char * string,char c)113 static 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
parse_option(char ** option_ptr,char ** value_ptr,char * line_str)127 int parse_option(char **option_ptr, char **value_ptr, char *line_str) {
128 return split_str(option_ptr, value_ptr, line_str, '=');
129 }
130
parse_path(char ** path_ptr,char ** prop_ptr,char * value_str)131 int parse_path(char **path_ptr, char **prop_ptr, char *value_str) {
132 return split_str(path_ptr, prop_ptr, value_str, ':');
133 }
134
get_fdt32_from_prop(void * fdt,const char * path,const char * prop)135 static 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
get_fdt32_from_number_or_prop(void * fdt,char * value_str)156 static 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
output_img_header(FILE * img_fp,uint32_t entry_count,uint32_t total_size,struct dt_global_options * options)173 static 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
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)188 static 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
234 end:
235 if (fdt) free(fdt);
236
237 return ret;
238 }
239
dt_image_writer_start(FILE * img_fp,uint32_t entry_count)240 struct 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
263 error:
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
set_dt_options(struct dt_options * options,const char * option,const char * value)272 static 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
set_global_options(struct dt_image_writer * writer,const char * option,const char * value)293 int 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
set_entry_options(struct dt_image_writer * writer,const char * option,const char * value)306 int 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
search_fdt_info(struct dt_image_writer * writer,const char * filename)311 static 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
add_fdt_info(struct dt_image_writer * writer,const char * filename,uint32_t dt_offset)322 static 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
flush_entry_to_img(struct dt_image_writer * writer)335 static 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
dt_image_writer_add_entry(struct dt_image_writer * writer,const char * fdt_filename)358 int 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
dt_image_writer_end(struct dt_image_writer * writer)377 int 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
395 end:
396 free(writer->fdt_infos);
397 free(writer);
398
399 return ret;
400 }
401