978 lines
32 KiB
C
978 lines
32 KiB
C
/*
|
|
* Copyright (c) 2012-2013, 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 QCDT_MAGIC "QCDT" /* Master DTB magic */
|
|
#define QCDT_VERSION 3 /* QCDT version */
|
|
|
|
#define QCDT_DT_TAG "qcom,msm-id = <"
|
|
#define QCDT_BOARD_TAG "qcom,board-id = <"
|
|
#define QCDT_PMIC_TAG "qcom,pmic-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
|
|
|
|
struct chipInfo_t {
|
|
uint32_t chipset;
|
|
uint32_t platform;
|
|
uint32_t subtype;
|
|
uint32_t revNum;
|
|
uint32_t pmic_model[4];
|
|
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;
|
|
|
|
struct chipId_t {
|
|
uint32_t chipset;
|
|
uint32_t revNum;
|
|
struct chipId_t *next;
|
|
struct chipId_t *t_next;
|
|
};
|
|
|
|
struct chipSt_t {
|
|
uint32_t platform;
|
|
uint32_t subtype;
|
|
struct chipSt_t *next;
|
|
struct chipSt_t *t_next;
|
|
};
|
|
|
|
struct chipPt_t {
|
|
uint32_t pmic0;
|
|
uint32_t pmic1;
|
|
uint32_t pmic2;
|
|
uint32_t pmic3;
|
|
struct chipPt_t *next;
|
|
struct chipPt_t *t_next;
|
|
};
|
|
|
|
char *input_dir;
|
|
char *output_file;
|
|
char *dtc_path;
|
|
int verbose;
|
|
int page_size = PAGE_SIZE_DEF;
|
|
|
|
|
|
void print_help()
|
|
{
|
|
log_info("dtbTool version %d\n", QCDT_VERSION);
|
|
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;
|
|
}
|
|
|
|
/* 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 ((c->chipset < x->chipset) ||
|
|
((c->chipset == x->chipset) &&
|
|
((c->platform < x->platform) ||
|
|
((c->platform == x->platform) &&
|
|
((c->subtype < x->subtype) ||
|
|
((c->subtype == x->subtype) &&
|
|
(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 ((c->chipset == x->chipset) &&
|
|
(c->platform == x->platform) &&
|
|
(c->subtype == x->subtype) &&
|
|
(c->revNum == x->revNum) &&
|
|
(c->pmic_model[0] == x->pmic_model[0]) &&
|
|
(c->pmic_model[1] == x->pmic_model[1]) &&
|
|
(c->pmic_model[2] == x->pmic_model[2]) &&
|
|
(c->pmic_model[3] == x->pmic_model[3])) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
For v1 Extract 'qcom,msm-id' parameter triplet from DTB
|
|
qcom,msm-id = <x y z>;
|
|
|
|
For v2 Extract 'qcom,msm-id', 'qcom,board-id' parameter double from DTB
|
|
qcom,msm-id = <x z> i.e chipset, revision number;
|
|
qcom,board-id = <y y'> i.e platform and sub-type;
|
|
*/
|
|
|
|
struct chipInfo_t *getChipInfo(const char *filename, int *num, uint32_t msmversion)
|
|
{
|
|
|
|
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, *tmp, *chip_t;
|
|
uint32_t data[3] = {0, 0, 0};
|
|
uint32_t data_st[2] = {0, 0};
|
|
uint32_t data_pt[4] = {0, 0, 0, 0};
|
|
char *tok, *sptr = NULL;
|
|
int i, entryValid, entryEnded;
|
|
int count = 0, count1 = 0, count2 = 0, count3 = 0;
|
|
int entryValidST, entryEndedST, entryValidDT, entryEndedDT, entryValidPT, entryEndedPT;
|
|
struct chipId_t *chipId = NULL, *cId = NULL, *tmp_id = NULL;
|
|
struct chipSt_t *chipSt = NULL, *cSt = NULL, *tmp_st = NULL;
|
|
struct chipPt_t *chipPt = NULL, *cPt = NULL, *tmp_pt = NULL;
|
|
struct chipId_t *chipId_tmp = NULL;
|
|
struct chipSt_t *chipSt_tmp = NULL;
|
|
struct chipPt_t *chipPt_tmp = NULL;
|
|
|
|
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 "qcom,msm-id" */
|
|
while ((llen = getline(&line, &line_size, pfile)) != -1) {
|
|
if (msmversion == 1) {
|
|
if ((pos = strstr(line, QCDT_DT_TAG)) != NULL) {
|
|
pos += strlen(QCDT_DT_TAG);
|
|
|
|
entryEnded = 0;
|
|
while (1) {
|
|
entryValid = 1;
|
|
for (i = 0; i < 3; i++) {
|
|
tok = strtok_r(pos, " \t", &sptr);
|
|
pos = NULL;
|
|
if (tok != NULL) {
|
|
if (*tok == '>') {
|
|
entryEnded = 1;
|
|
entryValid = 0;
|
|
break;
|
|
}
|
|
data[i] = strtoul(tok, NULL, 0);
|
|
} else {
|
|
data[i] = 0;
|
|
entryValid = 0;
|
|
entryEnded = 1;
|
|
}
|
|
}
|
|
if (entryEnded) {
|
|
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;
|
|
}
|
|
tmp->chipset = data[0];
|
|
tmp->platform = data[1];
|
|
tmp->subtype = 0;
|
|
tmp->revNum = data[2];
|
|
tmp->pmic_model[0] = 0;
|
|
tmp->pmic_model[1] = 0;
|
|
tmp->pmic_model[2] = 0;
|
|
tmp->pmic_model[3] = 0;
|
|
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", QCDT_DT_TAG);
|
|
break;
|
|
}
|
|
} else if (msmversion == 2 || msmversion == 3) {
|
|
if ((pos = strstr(line, QCDT_DT_TAG)) != NULL) {
|
|
pos += strlen(QCDT_DT_TAG);
|
|
|
|
entryEndedDT = 0;
|
|
for (;entryEndedDT < 1;) {
|
|
entryValidDT = 1;
|
|
for (i = 0; i < 2; i++) {
|
|
tok = strtok_r(pos, " \t", &sptr);
|
|
pos = NULL;
|
|
if (tok != NULL) {
|
|
if (*tok == '>') {
|
|
entryEndedDT = 1;
|
|
entryValidDT = 0;
|
|
break;
|
|
}
|
|
data_st[i] = strtoul(tok, NULL, 0);
|
|
} else {
|
|
data_st[i] = 0;
|
|
entryValidDT = 0;
|
|
entryEndedDT = 1;
|
|
}
|
|
}
|
|
|
|
if (entryValidDT) {
|
|
tmp_id = (struct chipId_t *)
|
|
malloc(sizeof(struct chipId_t));
|
|
if (!tmp_id) {
|
|
log_err("Out of memory\n");
|
|
break;
|
|
}
|
|
if (!chipId) {
|
|
chipId = tmp_id;
|
|
cId = tmp_id;
|
|
chipId->t_next = NULL;
|
|
} else {
|
|
tmp_id->t_next = chipId->t_next;
|
|
chipId->t_next = tmp_id;
|
|
}
|
|
tmp_id->chipset = data_st[0];
|
|
tmp_id->revNum= data_st[1];
|
|
count1++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((pos = strstr(line,QCDT_BOARD_TAG)) != NULL) {
|
|
pos += strlen(QCDT_BOARD_TAG);
|
|
entryEndedST = 0;
|
|
for (;entryEndedST < 1;) {
|
|
entryValidST = 1;
|
|
for (i = 0; i < 2; i++) {
|
|
tok = strtok_r(pos, " \t", &sptr);
|
|
pos = NULL;
|
|
if (tok != NULL) {
|
|
if (*tok == '>') {
|
|
entryEndedST = 1;
|
|
entryValidST = 0;
|
|
break;
|
|
}
|
|
data_st[i] = strtoul(tok, NULL, 0);
|
|
} else {
|
|
data_st[i] = 0;
|
|
entryValidST = 0;
|
|
entryEndedST = 1;
|
|
}
|
|
}
|
|
if (entryValidST) {
|
|
tmp_st = (struct chipSt_t *)
|
|
malloc(sizeof(struct chipSt_t));
|
|
if (!tmp_st) {
|
|
log_err("Out of memory\n");
|
|
break;
|
|
}
|
|
|
|
if (!chipSt) {
|
|
chipSt = tmp_st;
|
|
cSt = tmp_st;
|
|
chipSt->t_next = NULL;
|
|
} else {
|
|
tmp_st->t_next = chipSt->t_next;
|
|
chipSt->t_next = tmp_st;
|
|
}
|
|
|
|
tmp_st->platform = data_st[0];
|
|
tmp_st->subtype= data_st[1];
|
|
count2++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((pos = strstr(line,QCDT_PMIC_TAG)) != NULL) {
|
|
pos += strlen(QCDT_PMIC_TAG);
|
|
entryEndedPT = 0;
|
|
for (;entryEndedPT < 1;) {
|
|
entryValidPT = 1;
|
|
for (i = 0; i < 4; i++) {
|
|
tok = strtok_r(pos, " \t", &sptr);
|
|
pos = NULL;
|
|
if (tok != NULL) {
|
|
if (*tok == '>') {
|
|
entryEndedPT = 1;
|
|
entryValidPT = 0;
|
|
break;
|
|
}
|
|
data_pt[i] = strtoul(tok, NULL, 0);
|
|
} else {
|
|
data_pt[i] = 0;
|
|
entryValidPT = 0;
|
|
entryEndedPT = 1;
|
|
}
|
|
}
|
|
if (entryValidPT) {
|
|
tmp_pt = (struct chipPt_t *)
|
|
malloc(sizeof(struct chipPt_t));
|
|
if (!tmp_pt) {
|
|
log_err("Out of memory\n");
|
|
break;
|
|
}
|
|
|
|
if (!chipPt) {
|
|
chipPt = tmp_pt;
|
|
cPt = tmp_pt;
|
|
chipPt->t_next = NULL;
|
|
} else {
|
|
tmp_pt->t_next = chipPt->t_next;
|
|
chipPt->t_next = tmp_pt;
|
|
}
|
|
|
|
tmp_pt->pmic0 = data_pt[0];
|
|
tmp_pt->pmic1 = data_pt[1];
|
|
tmp_pt->pmic2 = data_pt[2];
|
|
tmp_pt->pmic3 = data_pt[3];
|
|
count3++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (line)
|
|
free(line);
|
|
|
|
if (count1 == 0) {
|
|
log_err("... skip, incorrect '%s' format\n", QCDT_DT_TAG);
|
|
return NULL;
|
|
}
|
|
if (count2 == 0) {
|
|
log_err("... skip, incorrect '%s' format\n", QCDT_BOARD_TAG);
|
|
return NULL;
|
|
}
|
|
if (count3 == 0 && msmversion == 3) {
|
|
log_err("... skip, incorrect '%s' format\n", QCDT_PMIC_TAG);
|
|
return NULL;
|
|
}
|
|
|
|
tmp_st = cSt;
|
|
tmp_pt = cPt;
|
|
while (cId != NULL) {
|
|
while (cSt != NULL) {
|
|
if (msmversion == 3) {
|
|
while (cPt != NULL) {
|
|
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;
|
|
}
|
|
|
|
tmp->chipset = cId->chipset;
|
|
tmp->platform = cSt->platform;
|
|
tmp->revNum = cId->revNum;
|
|
tmp->subtype = cSt->subtype;
|
|
tmp->pmic_model[0] = cPt->pmic0;
|
|
tmp->pmic_model[1] = cPt->pmic1;
|
|
tmp->pmic_model[2] = cPt->pmic2;
|
|
tmp->pmic_model[3] = cPt->pmic3;
|
|
tmp->dtb_size = 0;
|
|
tmp->dtb_file = NULL;
|
|
tmp->master = chip;
|
|
tmp->wroteDtb = 0;
|
|
tmp->master_offset = 0;
|
|
cPt = cPt->t_next;
|
|
}
|
|
cPt = tmp_pt;
|
|
} else {
|
|
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;
|
|
}
|
|
tmp->chipset = cId->chipset;
|
|
tmp->platform = cSt->platform;
|
|
tmp->revNum = cId->revNum;
|
|
tmp->subtype = cSt->subtype;
|
|
tmp->pmic_model[0] = 0;
|
|
tmp->pmic_model[1] = 0;
|
|
tmp->pmic_model[2] = 0;
|
|
tmp->pmic_model[3] = 0;
|
|
tmp->dtb_size = 0;
|
|
tmp->dtb_file = NULL;
|
|
tmp->master = chip;
|
|
tmp->wroteDtb = 0;
|
|
tmp->master_offset = 0;
|
|
}
|
|
cSt = cSt->t_next;
|
|
}
|
|
cSt = tmp_st;
|
|
cId = cId->t_next;
|
|
}
|
|
|
|
if (msmversion == 2)
|
|
entryEndedPT = 1;
|
|
|
|
/* clear memory*/
|
|
pclose(pfile);
|
|
while (chipId) {
|
|
chipId_tmp = chipId;
|
|
chipId = chipId->t_next;
|
|
free(chipId_tmp);
|
|
}
|
|
while (chipSt) {
|
|
chipSt_tmp= chipSt;
|
|
chipSt = chipSt->t_next;
|
|
free(chipSt_tmp);
|
|
}
|
|
|
|
while (chipPt) {
|
|
chipPt_tmp= chipPt;
|
|
chipPt = chipPt->t_next;
|
|
free(chipPt_tmp);
|
|
}
|
|
|
|
if (entryEndedST == 1 && entryEndedDT == 1 && entryEndedPT == 1) {
|
|
*num = count1;
|
|
return chip;
|
|
}
|
|
|
|
/* clear memory*/
|
|
while (chip) {
|
|
chip_t = chip;
|
|
chip = chip->next;
|
|
if (chip_t->dtb_file)
|
|
free(chip_t->dtb_file);
|
|
free(chip_t);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Get the version-id based on dtb files */
|
|
int GetVersionInfo(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;
|
|
int v = 1;
|
|
|
|
line_size = 1024;
|
|
line = (char *)malloc(line_size);
|
|
if (!line) {
|
|
log_err("Out of memory\n");
|
|
return 0;
|
|
}
|
|
|
|
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 0;
|
|
}
|
|
|
|
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 the type of version */
|
|
while ((llen = getline(&line, &line_size, pfile)) != -1) {
|
|
if ((pos = strstr(line,QCDT_BOARD_TAG)) != NULL) {
|
|
v = 2;
|
|
}
|
|
if ((pos = strstr(line,QCDT_PMIC_TAG)) != NULL) {
|
|
v = 3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(line);
|
|
log_info("Version:%d\n", v);
|
|
|
|
return v;
|
|
}
|
|
|
|
/* Extract 'qcom,msm-id' 'qcom,board-id' parameter from DTB
|
|
v1 format:
|
|
qcom,msm-id = <x y z> [, <x2 y2 z2> ...];
|
|
v2 format:
|
|
qcom,msm-id = <x z> [, <x2 z2> ...;
|
|
qcom,board-id = <y y'> [, <y2 y2'> ...;
|
|
Fields:
|
|
x = chipset
|
|
y = platform
|
|
y' = subtype
|
|
z = soc rev
|
|
*/
|
|
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 = QCDT_VERSION;
|
|
int num;
|
|
uint32_t dtb_size;
|
|
int msmversion = 0;
|
|
|
|
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 "qcom,msm-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 ... \n", 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);
|
|
|
|
/* To identify the version number */
|
|
msmversion = GetVersionInfo(filename);
|
|
|
|
num = 1;
|
|
chip = getChipInfo(filename, &num, msmversion);
|
|
|
|
if (msmversion == 1) {
|
|
if (!chip) {
|
|
log_err("skip, failed to scan for '%s' tag\n",
|
|
QCDT_DT_TAG);
|
|
free(filename);
|
|
continue;
|
|
}
|
|
}
|
|
if (msmversion == 2) {
|
|
if (!chip) {
|
|
log_err("skip, failed to scan for '%s' or '%s' tag\n",
|
|
QCDT_DT_TAG, QCDT_BOARD_TAG);
|
|
free(filename);
|
|
continue;
|
|
}
|
|
}
|
|
if (msmversion == 3) {
|
|
if (!chip) {
|
|
log_err("skip, failed to scan for '%s', '%s' or '%s' tag\n",
|
|
QCDT_DT_TAG, QCDT_BOARD_TAG, QCDT_PMIC_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: %u, rev: %u, platform: %u, subtype: %u, pmic0: %u, pmic1: %u, pmic2: %u, pmic3: %u\n",
|
|
chip->chipset, chip->revNum, chip->platform, chip->subtype,
|
|
chip->pmic_model[0], chip->pmic_model[1], chip->pmic_model[2], chip->pmic_model[3]);
|
|
|
|
for (t_chip = chip->t_next; t_chip; t_chip = t_chip->t_next) {
|
|
log_info("additional chipset: %u, rev: %u, platform: %u, subtype: %u, pmic0: %u, pmic1: %u, pmic2: %u, pmic3: %u\n",
|
|
t_chip->chipset, t_chip->revNum, t_chip->platform, t_chip->subtype,
|
|
t_chip->pmic_model[0], t_chip->pmic_model[1], t_chip->pmic_model[2], t_chip->pmic_model[3]);
|
|
}
|
|
|
|
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 %u, rev: %u, platform: %u, subtype %u:\n",
|
|
t_chip->chipset, t_chip->revNum, t_chip->platform, t_chip->subtype);
|
|
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, QCDT_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 */
|
|
(40 * 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
|
|
subtype
|
|
soc rev
|
|
pmic model0
|
|
pmic model1
|
|
pmic model2
|
|
pmic model3
|
|
dtb offset
|
|
dtb size
|
|
*/
|
|
for (chip = chip_list; chip; chip = chip->next) {
|
|
wrote += write(out_fd, &chip->chipset, sizeof(uint32_t));
|
|
wrote += write(out_fd, &chip->platform, sizeof(uint32_t));
|
|
wrote += write(out_fd, &chip->subtype, sizeof(uint32_t));
|
|
wrote += write(out_fd, &chip->revNum, sizeof(uint32_t));
|
|
wrote += write(out_fd, &chip->pmic_model[0], sizeof(uint32_t));
|
|
wrote += write(out_fd, &chip->pmic_model[1], sizeof(uint32_t));
|
|
wrote += write(out_fd, &chip->pmic_model[2], sizeof(uint32_t));
|
|
wrote += write(out_fd, &chip->pmic_model[3], sizeof(uint32_t));
|
|
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;
|
|
}
|