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