| /* |
| * Copyright (c) 2012, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the following |
| disclaimer in the documentation and/or other materials provided |
| with the distribution. |
| * Neither the name of The Linux Foundation nor the names of its |
| contributors may be used to endorse or promote products derived |
| from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #define _GNU_SOURCE |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <dirent.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <errno.h> |
| #include <unistd.h> |
| |
| #define AML_DT_MAGIC "AML_" /* Master DTB magic */ |
| #define AML_DT_VERSION 2 /* AML_ version */ |
| |
| #define AML_DT_TAG "amlogic-dt-id = \"" |
| |
| #define PAGE_SIZE_DEF 2048 |
| #define PAGE_SIZE_MAX (1024*1024) |
| |
| #define log_err(x...) printf(x) |
| #define log_info(x...) printf(x) |
| #define log_dbg(x...) { if (verbose) printf(x); } |
| |
| #define COPY_BLK 1024 /* File copy block size */ |
| |
| #define RC_SUCCESS 0 |
| #define RC_ERROR -1 |
| |
| #define AML_DT_IND_LENGTH 16 |
| #define AML_DT_IND_INT_LENGTH 4 //one int store 4 char |
| #define AML_DT_IND_INT_NUM (AML_DT_IND_LENGTH / AML_DT_IND_INT_LENGTH) |
| #define MULTI_DTB_HEADER_SIZE 12 |
| #define SINGLE_DTB_HEADER_SIZE (8+AML_DT_IND_INT_NUM*12) |
| |
| struct chipInfo_t { |
| uint32_t chipset[AML_DT_IND_INT_NUM]; |
| uint32_t platform[AML_DT_IND_INT_NUM]; |
| uint32_t revNum[AML_DT_IND_INT_NUM]; |
| uint32_t dtb_size; |
| char *dtb_file; |
| struct chipInfo_t *prev; |
| struct chipInfo_t *next; |
| struct chipInfo_t *master; |
| int wroteDtb; |
| uint32_t master_offset; |
| struct chipInfo_t *t_next; |
| }; |
| |
| struct chipInfo_t *chip_list; |
| |
| char *input_dir; |
| char *output_file; |
| char *dtc_path; |
| int verbose; |
| int page_size = PAGE_SIZE_DEF; |
| |
| void print_help() |
| { |
| log_info("dtbTool [options] -o <output file> <input DTB path>\n"); |
| log_info(" options:\n"); |
| log_info(" --output-file/-o output file\n"); |
| log_info(" --dtc-path/-p path to dtc\n"); |
| log_info(" --page-size/-s page size in bytes\n"); |
| log_info(" --verbose/-v verbose\n"); |
| log_info(" --help/-h this help screen\n"); |
| } |
| |
| int parse_commandline(int argc, char *const argv[]) |
| { |
| int c; |
| |
| struct option long_options[] = { |
| {"output-file", 1, 0, 'o'}, |
| {"dtc-path", 1, 0, 'p'}, |
| {"page-size", 1, 0, 's'}, |
| {"verbose", 0, 0, 'v'}, |
| {"help", 0, 0, 'h'}, |
| {0, 0, 0, 0} |
| }; |
| |
| while ((c = getopt_long(argc, argv, "-o:p:s:vh", long_options, NULL)) |
| != -1) { |
| switch (c) { |
| case 1: |
| if (!input_dir) |
| input_dir = optarg; |
| break; |
| case 'o': |
| output_file = optarg; |
| break; |
| case 'p': |
| dtc_path = optarg; |
| break; |
| case 's': |
| page_size = atoi(optarg); |
| if ((page_size <= 0) || (page_size > (PAGE_SIZE_MAX))) { |
| log_err("Invalid page size (> 0 and <=1MB\n"); |
| return RC_ERROR; |
| } |
| break; |
| case 'v': |
| verbose = 1; |
| break; |
| case 'h': |
| default: |
| return RC_ERROR; |
| } |
| } |
| |
| if (!output_file) { |
| log_err("Output file must be specified\n"); |
| return RC_ERROR; |
| } |
| |
| if (!input_dir) |
| input_dir = "./"; |
| |
| if (!dtc_path) |
| dtc_path = ""; |
| |
| return RC_SUCCESS; |
| } |
| |
| int cmp_ints(unsigned int *a, unsigned int *b) |
| { |
| int i = 0; |
| for(i=0; i<AML_DT_IND_INT_NUM; i++){ |
| //printf("a:%x,b:%x",a[i],b[i]); |
| if(a[i] < b[i]) |
| return -1; |
| if(a[i] > b[i]) |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* Unique entry sorted list add (by chipset->platform->rev) */ |
| int chip_add(struct chipInfo_t *c) |
| { |
| struct chipInfo_t *x = chip_list; |
| |
| if (!chip_list) { |
| chip_list = c; |
| c->next = NULL; |
| c->prev = NULL; |
| return RC_SUCCESS; |
| } |
| |
| while (1) { |
| if ((-1 == cmp_ints(c->chipset, x->chipset)) || \ |
| ((0 == cmp_ints(c->chipset, x->chipset)) && (-1 == cmp_ints(c->platform, x->platform))) || \ |
| ((0 == cmp_ints(c->platform, x->platform)) && (-1 == cmp_ints(c->revNum, x->revNum)))){ |
| if (!x->prev) { |
| c->next = x; |
| c->prev = NULL; |
| x->prev = c; |
| chip_list = c; |
| break; |
| } else { |
| c->next = x; |
| c->prev = x->prev; |
| x->prev->next = c; |
| x->prev = c; |
| break; |
| } |
| } |
| if ((0 == cmp_ints(c->chipset, x->chipset)) && \ |
| (0 == cmp_ints(c->platform, x->platform)) && \ |
| (0 == cmp_ints(c->revNum, x->revNum))){ |
| return RC_ERROR; /* duplicate */ |
| } |
| if (!x->next) { |
| c->prev = x; |
| c->next = NULL; |
| x->next = c; |
| break; |
| } |
| x = x->next; |
| } |
| return RC_SUCCESS; |
| } |
| |
| void chip_deleteall() |
| { |
| struct chipInfo_t *c = chip_list, *t; |
| |
| while (c) { |
| t = c; |
| c = c->next; |
| if (t->dtb_file) |
| free(t->dtb_file); |
| free(t); |
| } |
| } |
| |
| /* Extract 'amlogic-dt-id' parameter triplet from DTB |
| amlogic-dt-id = <x y z>; |
| */ |
| struct chipInfo_t *getChipInfo(const char *filename, int *num) |
| { |
| const char str1[] = "dtc -I dtb -O dts \""; |
| const char str2[] = "\" 2>&1"; |
| char *buf, *pos; |
| char *line = NULL; |
| size_t line_size; |
| FILE *pfile; |
| int llen,loop; |
| struct chipInfo_t *chip = NULL, *tmp; |
| uint32_t data[3][AML_DT_IND_INT_NUM] = {0}; |
| char *tok, *sptr = NULL; |
| int i, count = 0, entryValid, entryEnded; |
| |
| line_size = 1024; |
| line = (char *)malloc(line_size); |
| if (!line) { |
| log_err("Out of memory\n"); |
| return NULL; |
| } |
| |
| llen = sizeof(char) * (strlen(dtc_path) + |
| strlen(str1) + |
| strlen(str2) + |
| strlen(filename) + 1); |
| buf = (char *)malloc(llen); |
| if (!buf) { |
| log_err("Out of memory\n"); |
| free(line); |
| return NULL; |
| } |
| |
| strncpy(buf, dtc_path, llen); |
| strncat(buf, str1, llen); |
| strncat(buf, filename, llen); |
| strncat(buf, str2, llen); |
| |
| pfile = popen(buf, "r"); |
| free(buf); |
| |
| if (pfile == NULL) { |
| log_err("... skip, fail to decompile dtb\n"); |
| } else { |
| /* Find "amlogic-dt-id" */ |
| while ((llen = getline(&line, &line_size, pfile)) != -1) { |
| if ((pos = strstr(line, AML_DT_TAG)) != NULL) { |
| pos += strlen(AML_DT_TAG); |
| |
| entryEnded = 0; |
| while (1) { |
| entryValid = 1; |
| for (i = 0; i < 3; i++) { |
| tok = strtok_r(pos, "_", &sptr); |
| pos = NULL; |
| int j = 0; |
| if (tok != NULL) { |
| if (*tok == '\"') { |
| entryEnded = 1; |
| entryValid = 0; |
| break; |
| } |
| int str_len = strlen(tok); |
| if(str_len > AML_DT_IND_LENGTH) |
| str_len = AML_DT_IND_LENGTH; |
| for(j = 0; j < str_len; j++){ |
| if(tok[j] == '\"') |
| break; |
| data[i][(int)(j/AML_DT_IND_INT_LENGTH)] |= (tok[j] << ((3-(int)(j%AML_DT_IND_INT_LENGTH)) * 8)); |
| } |
| for(j = j; j < AML_DT_IND_LENGTH; j++){ |
| data[i][(int)(j/AML_DT_IND_INT_LENGTH)] |= (' ' << ((3-(int)(j%AML_DT_IND_INT_LENGTH)) * 8)); |
| } |
| } |
| else { |
| for(j=0; j<AML_DT_IND_INT_NUM; j++) |
| data[i][j] = 0; |
| entryValid = 0; |
| entryEnded = 1; |
| } |
| } |
| if (entryEnded) { |
| if(line) |
| free(line); |
| pclose(pfile); |
| *num = count; |
| return chip; |
| } |
| if (entryValid) { |
| tmp = (struct chipInfo_t *) |
| malloc(sizeof(struct chipInfo_t)); |
| if (!tmp) { |
| log_err("Out of memory\n"); |
| break; |
| } |
| if (!chip) { |
| chip = tmp; |
| chip->t_next = NULL; |
| } else { |
| tmp->t_next = chip->t_next; |
| chip->t_next = tmp; |
| } |
| for(loop = 0; loop<AML_DT_IND_INT_NUM; loop++){ |
| tmp->chipset[loop] = data[0][loop]; |
| } |
| for(loop = 0; loop<AML_DT_IND_INT_NUM; loop++){ |
| tmp->platform[loop] = data[1][loop]; |
| } |
| for(loop = 0; loop<AML_DT_IND_INT_NUM; loop++){ |
| tmp->revNum[loop] = data[2][loop]; |
| } |
| tmp->dtb_size = 0; |
| tmp->dtb_file = NULL; |
| tmp->master = chip; |
| tmp->wroteDtb = 0; |
| tmp->master_offset = 0; |
| count++; |
| } |
| } |
| |
| log_err("... skip, incorrect '%s' format\n", AML_DT_TAG); |
| break; |
| } |
| } |
| if (line) |
| free(line); |
| pclose(pfile); |
| } |
| |
| return NULL; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| char buf[COPY_BLK]; |
| struct chipInfo_t *chip, *t_chip; |
| struct dirent *dp; |
| FILE *pInputFile; |
| char *filename; |
| int padding; |
| uint8_t *filler = NULL; |
| int numBytesRead = 0; |
| int totBytesRead = 0; |
| int out_fd; |
| int flen; |
| int rc = RC_SUCCESS; |
| int dtb_count = 0, dtb_offset = 0; |
| size_t wrote = 0, expected = 0; |
| struct stat st; |
| uint32_t version = AML_DT_VERSION; |
| int num; |
| uint32_t dtb_size; |
| |
| log_info("DTB combiner:\n"); |
| |
| if (parse_commandline(argc, argv) != RC_SUCCESS) { |
| print_help(); |
| return RC_ERROR; |
| } |
| |
| log_info(" Input directory: '%s'\n", input_dir); |
| log_info(" Output file: '%s'\n", output_file); |
| |
| DIR *dir = opendir(input_dir); |
| if (!dir) { |
| log_err("Failed to open input directory '%s'\n", input_dir); |
| return RC_ERROR; |
| } |
| |
| filler = (uint8_t *)malloc(page_size); |
| if (!filler) { |
| log_err("Out of memory\n"); |
| closedir(dir); |
| return RC_ERROR; |
| } |
| memset(filler, 0, page_size); |
| |
| /* Open the .dtb files in the specified path, decompile and |
| extract "amlogic-dt-id" parameter |
| */ |
| while ((dp = readdir(dir)) != NULL) { |
| if ((dp->d_type == DT_REG)) { |
| flen = strlen(dp->d_name); |
| if ((flen > 4) && (strncmp(&dp->d_name[flen-4], ".dtb", 4) == 0)) { |
| log_info("Found file: %s ... ", dp->d_name); |
| |
| flen = strlen(input_dir) + strlen(dp->d_name) + 1; |
| filename = (char *)malloc(flen); |
| if (!filename) { |
| log_err("Out of memory\n"); |
| rc = RC_ERROR; |
| break; |
| } |
| strncpy(filename, input_dir, flen); |
| strncat(filename, dp->d_name, flen); |
| |
| num = 1; |
| chip = getChipInfo(filename, &num); |
| if (!chip) { |
| log_err("skip, failed to scan for '%s' tag\n", AML_DT_TAG); |
| free(filename); |
| continue; |
| } |
| |
| if ((stat(filename, &st) != 0) || |
| (st.st_size == 0)) { |
| log_err("skip, failed to get DTB size\n"); |
| free(filename); |
| continue; |
| } |
| int i,j; |
| log_info(" chipset: "); |
| for(i=0; i<AML_DT_IND_INT_NUM; i++){ |
| for(j=0;j<4;j++) |
| log_info("%c", ((chip->chipset[i]>>((3-j)*8))&0xff)); |
| } |
| log_info(" platform: "); |
| for(i=0; i<AML_DT_IND_INT_NUM; i++){ |
| for(j=0;j<4;j++) |
| log_info("%c", ((chip->platform[i]>>((3-j)*8))&0xff)); |
| } |
| log_info(" rev: "); |
| for(i=0; i<AML_DT_IND_INT_NUM; i++){ |
| for(j=0;j<4;j++) |
| log_info("%c", ((chip->revNum[i]>>((3-j)*8))&0xff)); |
| } |
| log_info("\n"); |
| #if 0 |
| for (t_chip = chip->t_next; t_chip; t_chip = t_chip->t_next) { |
| log_info(" additional chipset: %x, platform: %x, rev: %x\n", |
| t_chip->chipset, t_chip->platform, t_chip->revNum); |
| } |
| #endif |
| |
| rc = chip_add(chip); |
| if (rc != RC_SUCCESS) { |
| log_err("... duplicate info, skipped\n"); |
| free(filename); |
| continue; |
| } |
| |
| dtb_count++; |
| |
| chip->dtb_size = st.st_size + |
| (page_size - (st.st_size % page_size)); |
| chip->dtb_file = filename; |
| |
| for (t_chip = chip->t_next; t_chip; t_chip = t_chip->t_next) { |
| rc = chip_add(t_chip); |
| if (rc != RC_SUCCESS) { |
| //log_err("... duplicate info, skipped (chipset %x, platform: %x, rev: %x\n", |
| //t_chip->chipset, t_chip->platform, t_chip->revNum); |
| log_err("... duplicate info, skipped...\n"); |
| continue; |
| } |
| dtb_count++; |
| } |
| } |
| } |
| } |
| closedir(dir); |
| log_info("=> Found %d unique DTB(s)\n", dtb_count); |
| |
| if (!dtb_count) |
| goto cleanup; |
| |
| |
| /* Generate the master DTB file: |
| |
| Simplify write error handling by just checking for actual vs |
| expected bytes written at the end. |
| */ |
| |
| log_info("\nGenerating master DTB... "); |
| |
| out_fd = open(output_file, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); |
| if (!out_fd < 0) { |
| log_err("Cannot create '%s'\n", output_file); |
| rc = RC_ERROR; |
| goto cleanup; |
| } |
| |
| /* Write header info */ |
| wrote += write(out_fd, AML_DT_MAGIC, sizeof(uint8_t) * 4); /* magic */ |
| wrote += write(out_fd, &version, sizeof(uint32_t)); /* version */ |
| wrote += write(out_fd, (uint32_t *)&dtb_count, sizeof(uint32_t)); |
| /* #DTB */ |
| |
| /* Calculate offset of first DTB block */ |
| dtb_offset = MULTI_DTB_HEADER_SIZE + /* header */ |
| (SINGLE_DTB_HEADER_SIZE * dtb_count) + /* DTB table entries */ |
| 4; /* end of table indicator */ |
| /* Round up to page size */ |
| padding = page_size - (dtb_offset % page_size); |
| dtb_offset += padding; |
| expected = dtb_offset; |
| |
| /* Write index table: |
| chipset |
| platform |
| soc rev |
| dtb offset |
| dtb size |
| */ |
| for (chip = chip_list; chip; chip = chip->next) { |
| wrote += write(out_fd, &chip->chipset[0], sizeof(uint32_t)*AML_DT_IND_INT_NUM); |
| wrote += write(out_fd, &chip->platform[0], sizeof(uint32_t)*AML_DT_IND_INT_NUM); |
| wrote += write(out_fd, &chip->revNum[0], sizeof(uint32_t)*AML_DT_IND_INT_NUM); |
| if (chip->master->master_offset != 0) { |
| wrote += write(out_fd, &chip->master->master_offset, sizeof(uint32_t)); |
| } else { |
| wrote += write(out_fd, &expected, sizeof(uint32_t)); |
| chip->master->master_offset = expected; |
| expected += chip->master->dtb_size; |
| } |
| wrote += write(out_fd, &chip->master->dtb_size, sizeof(uint32_t)); |
| } |
| |
| rc = RC_SUCCESS; |
| wrote += write(out_fd, &rc, sizeof(uint32_t)); /* end of table indicator */ |
| if (padding > 0) |
| wrote += write(out_fd, filler, padding); |
| |
| /* Write DTB's */ |
| for (chip = chip_list; chip; chip = chip->next) { |
| if (chip->master->wroteDtb) { |
| continue; |
| } |
| |
| chip->master->wroteDtb = 1; |
| filename = chip->master->dtb_file; |
| dtb_size = chip->master->dtb_size; |
| |
| log_dbg("\n (writing '%s' - %u bytes) ", filename, dtb_size); |
| pInputFile = fopen(filename, "r"); |
| if (pInputFile != NULL) { |
| totBytesRead = 0; |
| while ((numBytesRead = fread(buf, 1, COPY_BLK, pInputFile)) > 0) { |
| wrote += write(out_fd, buf, numBytesRead); |
| totBytesRead += numBytesRead; |
| } |
| fclose(pInputFile); |
| padding = page_size - (totBytesRead % page_size); |
| if ((uint32_t)(totBytesRead + padding) != dtb_size) { |
| log_err("DTB size mismatch, please re-run: expected %d vs actual %d (%s)\n", |
| dtb_size, totBytesRead + padding, |
| filename); |
| rc = RC_ERROR; |
| break; |
| } |
| if (padding > 0) |
| wrote += write(out_fd, filler, padding); |
| } else { |
| log_err("failed to open DTB '%s'\n", filename); |
| rc = RC_ERROR; |
| break; |
| } |
| } |
| close(out_fd); |
| |
| if (expected != wrote) { |
| log_err("error writing output file, please rerun: size mismatch %d vs %d\n", |
| expected, wrote); |
| rc = RC_ERROR; |
| } else |
| log_dbg("Total wrote %u bytes\n", wrote); |
| |
| if (rc != RC_SUCCESS) |
| unlink(output_file); |
| else |
| log_info("completed\n"); |
| |
| cleanup: |
| free(filler); |
| chip_deleteall(); |
| return rc; |
| } |