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