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