mkdtimg_core.c revision af1303611fdc1f961dd221e3c267ec572fa8a779
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_fdt_info { 44 char filename[1024]; 45 uint32_t dt_offset; 46}; 47 48struct 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 64static void init_dt_options(struct dt_options *options) { 65 memset(options, 0, sizeof(struct dt_options)); 66} 67 68static 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 73static void copy_dt_options(struct dt_options *target, struct dt_options *options) { 74 memcpy(target, options, sizeof(struct dt_options)); 75} 76 77static 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 100static 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 113static 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 127int parse_option(char **option_ptr, char **value_ptr, char *line_str) { 128 return split_str(option_ptr, value_ptr, line_str, '='); 129} 130 131int parse_path(char **path_ptr, char **prop_ptr, char *value_str) { 132 return split_str(path_ptr, prop_ptr, value_str, ':'); 133} 134 135static 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 156static 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 173static 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 188static 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 234end: 235 if (fdt) free(fdt); 236 237 return ret; 238} 239 240struct 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 263error: 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 272static 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 293int 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 306int 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 311static 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 322static 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 335static 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 358int 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 377int 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 395end: 396 free(writer->fdt_infos); 397 free(writer); 398 399 return ret; 400} 401