From 3527bd913e1445a310edb9884e34559f0e4a3055 Mon Sep 17 00:00:00 2001 From: Wilhansen Li Date: Mon, 26 Dec 2016 20:38:08 +0800 Subject: [PATCH 1/1] Initial import. --- Makefile | 7 + README.md | 8 + dtbSplit.cpp | 175 ++++++++++++++++++ dtbTool.c | 502 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 692 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 dtbSplit.cpp create mode 100644 dtbTool.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c28230b --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +all: dtbTool dtbSplit + +dtbTool: dtbTool.c + $(CC) -o dtbTool dtbTool.c + +dtbSplit: dtbSplit.cpp + $(CXX) -o dtbSplit -std=c++11 dtbSplit.cpp diff --git a/README.md b/README.md new file mode 100644 index 0000000..f3af6ad --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +AML DTB Tools +==== + +Comprises of a splitter and builder of AmLogic's multi-dtb formats. + +The format is deduced from the [AmLogic u-boot code](https://github.com/codesnake/uboot-amlogic/blob/master/common/aml_dt.c) + +The dtbTool is based on dtbTool source from the CyanogenMod project. diff --git a/dtbSplit.cpp b/dtbSplit.cpp new file mode 100644 index 0000000..ae4afe4 --- /dev/null +++ b/dtbSplit.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2016 Wilhansen Li. 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 Wilhansen Li 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. + */ + +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#define AML_DT_HEADER 0x5f4c4d41 +#define DT_HEADER_MAGIC 0xedfe0dd0 +#define AML_DT_ID_VARI_TOTAL 3 + +struct DTHeader { + uint32_t magic; /* magic word of OF_DT_HEADER */ + uint32_t totalsize; /* total size of DT block */ +}; +struct Header { + uint32_t magic; + uint32_t version; + uint32_t entry_count; +}; + +template +struct HeaderEntry { + char soc[ID_SIZE]; + char plat[ID_SIZE]; + char vari[ID_SIZE]; + uint32_t offset; + char padding[4]; +}; + +typedef HeaderEntry<4> HeaderEntryV1; +typedef HeaderEntry<16> HeaderEntryV2; + +void trimSpace(char *b, const int len) { + int len2 = len; + while (len2 > 0 && isspace(b[len2 - 1])) { + len2--; + } + if (len2 < len && len2 > 0) { + b[len2] = 0; + b[len - 1] = 0; + } +} +template +void dumpData(const uint32_t entries, const string &dest, ifstream &dtb) { + typedef HeaderEntry HeaderType; + + vector headers; + for ( uint32_t i = 0; i < entries; ++i ) { + HeaderType h; + dtb.read((char*)&h, sizeof(h)); + + headers.push_back(h); + } + for ( uint32_t i = 0; i < headers.size(); ++i ) { + auto &h = headers[i]; + ostringstream id; + + auto u32soc = reinterpret_cast(h.soc); + auto u32plat = reinterpret_cast(h.plat); + auto u32vari = reinterpret_cast(h.vari); + for ( uint32_t j = 0; j < ID_SIZE/sizeof(uint32_t); ++j ) { + *(u32soc + j) = ntohl(*(u32soc + j)); + *(u32plat + j) = ntohl(*(u32plat + j)); + *(u32vari + j) = ntohl(*(u32vari + j)); + } + trimSpace(h.soc, ID_SIZE); + trimSpace(h.plat, ID_SIZE); + trimSpace(h.vari, ID_SIZE); + + if ( h.soc[ID_SIZE-1] == 0 ) { + id << h.soc; + } else { + id.write(h.soc, sizeof(h.soc)); + } + id << '-'; + if ( h.plat[ID_SIZE-1] == 0 ) { + id << h.plat; + } else { + id.write(h.plat, sizeof(h.plat)); + } + id << '-'; + if ( h.vari[ID_SIZE-1] == 0 ) { + id << h.vari; + } else { + id.write(h.vari, sizeof(h.vari)); + } + cout << "Found header: " << id.str() << '\n'; + + dtb.seekg(h.offset); + DTHeader dtheader; + dtb.read((char*)&dtheader, sizeof(dtheader)); + if ( dtheader.magic != DT_HEADER_MAGIC ) { + cout.setf(ios::hex); + cout << "\tDTB Header mismatch. Found: " << dtheader.magic; + continue; + } + dtheader.totalsize = ntohl(dtheader.totalsize); + cout.setf(ios::dec); + cout << "\t offset: " << h.offset << " size: " << dtheader.totalsize << '\n'; + dtb.seekg(h.offset); + vector data(dtheader.totalsize); + dtb.read(data.data(), data.size()); + ofstream output(dest + id.str() + ".dtb", ios::binary); + output.write(data.data(), data.size()); + } + +} + +int main(int argc, char **argv) { + if ( argc < 3 ) { + cerr << "Usage: " << argv[0] << " boot.img out_prefix\n"; + return 1; + } + + ifstream dtb(argv[1], ios::binary); + if ( !dtb ) { + cerr << "Unable to open dtb file: " << argv[2] << endl; + return 1; + } + string dest; + if ( argc > 2 ) { + dest = argv[2]; + } + Header header; + dtb.read((char*)&header, sizeof(header)); + + if ( header.magic != AML_DT_HEADER ) { + cerr << "Invalid AML DTB header." << endl; + return 1; + } + cout << "DTB Version: " << header.version << " entries: " << header.entry_count << endl; + + if(header.version == 1) { + dumpData<4>(header.entry_count, dest, dtb); + } else if(header.version == 2) { + dumpData<16>(header.entry_count, dest, dtb); + } else { + cerr << "Unrecognized DTB version" << endl; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/dtbTool.c b/dtbTool.c new file mode 100644 index 0000000..b5a1a8f --- /dev/null +++ b/dtbTool.c @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. 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 Code Aurora Forum, Inc. 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AML_DT_MAGIC "AML_" /* Master DTB magic */ +#define AML_DT_VERSION 2 /* AML version */ + +#define DT_ID_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 INFO_ENTRY_SIZE 16 +#define INFO_ENTRY_SIZE_S "16" +#define TABLE_ENTRY_HEADER_SIZE (INFO_ENTRY_SIZE * 3 + sizeof(uint32_t) * 2) + +struct chipInfo_t { + uint8_t chipset[INFO_ENTRY_SIZE]; + uint8_t platform[INFO_ENTRY_SIZE]; + uint8_t revNum[INFO_ENTRY_SIZE]; + uint32_t dtb_size; + char *dtb_file; + struct chipInfo_t *prev; + struct chipInfo_t *next; +}; + +struct chipInfo_t *chip_list; + +char *input_dir; +char *output_file; +char *dtc_path; +int verbose; +int page_size = PAGE_SIZE_DEF; + +int entry_cmp(uint8_t *a, uint8_t *b) +{ + return memcmp(a, b, INFO_ENTRY_SIZE); +} + +void print_help() +{ + log_info("dtbTool [options] -o \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; +} + +/* 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 (entry_cmp(c->chipset, x->chipset) < 0 || + (entry_cmp(c->chipset, x->chipset) == 0 && + (entry_cmp(c->platform, x->platform) < 0 || + (entry_cmp(c->platform, x->platform) == 0 && + entry_cmp(c->revNum, x->revNum) < 0)))) { + if (!x->prev) { + c->next = chip_list; + c->prev = NULL; + chip_list = c; + break; + } else { + c->next = x; + c->prev = x->prev; + x->prev = c; + break; + } + } + if (entry_cmp(c->chipset, x->chipset) == 0 && + entry_cmp(c->platform, x->platform) == 0 && + entry_cmp(c->revNum, x->revNum) == 0) { + 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; + free(t->dtb_file); + free(t); + } +} + +/* Extract 'qcom,msm-id' parameter triplet from DTB + qcom,msm-id = ; + */ +struct chipInfo_t *getChipInfo(const char *filename) +{ + 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; + struct chipInfo_t *chip = NULL; + int rc = 0; + uint8_t data[3][INFO_ENTRY_SIZE + 1] = { {0} }; + + 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 "dtb file entry" */ + while ((llen = getline(&line, &line_size, pfile)) != -1) { + if ((pos = strstr(line, DT_ID_TAG)) != NULL) { + pos += strlen(DT_ID_TAG); + + for (; *pos; pos++) { + if (*pos == '"') { + /* Start convertion of triplet */ + pos++; + rc = sscanf(pos, "%" INFO_ENTRY_SIZE_S "[^_]_%" INFO_ENTRY_SIZE_S "[^_]_%" INFO_ENTRY_SIZE_S "[^_\"]\"", + data[0], data[1], data[2]); + if (rc == 3) { + chip = (struct chipInfo_t *) + malloc(sizeof(struct chipInfo_t)); + if (!chip) { + log_err("Out of memory\n"); + break; + } + memcpy(chip->chipset, data[0], INFO_ENTRY_SIZE); + memcpy(chip->platform, data[1], INFO_ENTRY_SIZE); + memcpy(chip->revNum, data[2], INFO_ENTRY_SIZE); + chip->dtb_size = 0; + chip->dtb_file = NULL; + + free(line); + pclose(pfile); + return chip; + } else { + break; + } + } + } + log_err("... skip, incorrect '%s' format\n", DT_ID_TAG); + break; + } + } + if (line) + free(line); + pclose(pfile); + } + + return NULL; +} + +int main(int argc, char **argv) +{ + char buf[COPY_BLK]; + struct chipInfo_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; + + 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); + + chip = getChipInfo(filename); + if (!chip) { + log_err("skip, failed to scan for '%s' tag\n", + DT_ID_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; + } + + log_info("chipset: %" INFO_ENTRY_SIZE_S "s, " + "platform: %" INFO_ENTRY_SIZE_S "s, " + "rev: %" INFO_ENTRY_SIZE_S "s\n", + chip->chipset, chip->platform, chip->revNum); + + 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; + } + } + } + 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 | O_TRUNC, 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 = 12 + /* header */ + (TABLE_ENTRY_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, INFO_ENTRY_SIZE); + wrote += write(out_fd, &chip->platform, INFO_ENTRY_SIZE); + wrote += write(out_fd, &chip->revNum, INFO_ENTRY_SIZE); + wrote += write(out_fd, &expected, sizeof(uint32_t)); + wrote += write(out_fd, &chip->dtb_size, sizeof(uint32_t)); + expected += chip->dtb_size; + } + + 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) { + log_dbg("\n (writing '%s' - %u bytes) ", chip->dtb_file, chip->dtb_size); + pInputFile = fopen(chip->dtb_file, "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) != chip->dtb_size) { + log_err("DTB size mismatch, please re-run: expected %d vs actual %d (%s)\n", + chip->dtb_size, totBytesRead + padding, + chip->dtb_file); + rc = RC_ERROR; + break; + } + if (padding > 0) + wrote += write(out_fd, filler, padding); + } else { + log_err("failed to open DTB '%s'\n", chip->dtb_file); + 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; +} \ No newline at end of file -- 2.20.1