/*
|
* (C) Copyright 2008-2015 Fuzhou Rockchip Electronics Co., Ltd
|
*
|
* SPDX-License-Identifier: GPL-2.0+
|
*/
|
#include "boot_merger.h"
|
#include <time.h>
|
#include <sys/stat.h>
|
#include <version.h>
|
|
/* #define USE_P_RC4 */
|
|
bool gDebug =
|
#ifdef DEBUG
|
true;
|
#else
|
false;
|
#endif /* DEBUG */
|
|
#define ENTRY_ALIGN (2048)
|
options gOpts;
|
char gLegacyPath[MAX_LINE_LEN] = { 0 };
|
char gNewPath[MAX_LINE_LEN] = { 0 };
|
static char *gPrePath;
|
char gSubfix[MAX_LINE_LEN] = OUT_SUBFIX;
|
char gEat[MAX_LINE_LEN];
|
char *gConfigPath;
|
uint8_t *gBuf;
|
bool enableRC4 = false;
|
|
static uint32_t g_merge_max_size = MAX_MERGE_SIZE;
|
|
uint32_t gTable_Crc32[256] = {
|
0x00000000, 0x04c10db7, 0x09821b6e, 0x0d4316d9, 0x130436dc, 0x17c53b6b,
|
0x1a862db2, 0x1e472005, 0x26086db8, 0x22c9600f, 0x2f8a76d6, 0x2b4b7b61,
|
0x350c5b64, 0x31cd56d3, 0x3c8e400a, 0x384f4dbd, 0x4c10db70, 0x48d1d6c7,
|
0x4592c01e, 0x4153cda9, 0x5f14edac, 0x5bd5e01b, 0x5696f6c2, 0x5257fb75,
|
0x6a18b6c8, 0x6ed9bb7f, 0x639aada6, 0x675ba011, 0x791c8014, 0x7ddd8da3,
|
0x709e9b7a, 0x745f96cd, 0x9821b6e0, 0x9ce0bb57, 0x91a3ad8e, 0x9562a039,
|
0x8b25803c, 0x8fe48d8b, 0x82a79b52, 0x866696e5, 0xbe29db58, 0xbae8d6ef,
|
0xb7abc036, 0xb36acd81, 0xad2ded84, 0xa9ece033, 0xa4aff6ea, 0xa06efb5d,
|
0xd4316d90, 0xd0f06027, 0xddb376fe, 0xd9727b49, 0xc7355b4c, 0xc3f456fb,
|
0xceb74022, 0xca764d95, 0xf2390028, 0xf6f80d9f, 0xfbbb1b46, 0xff7a16f1,
|
0xe13d36f4, 0xe5fc3b43, 0xe8bf2d9a, 0xec7e202d, 0x34826077, 0x30436dc0,
|
0x3d007b19, 0x39c176ae, 0x278656ab, 0x23475b1c, 0x2e044dc5, 0x2ac54072,
|
0x128a0dcf, 0x164b0078, 0x1b0816a1, 0x1fc91b16, 0x018e3b13, 0x054f36a4,
|
0x080c207d, 0x0ccd2dca, 0x7892bb07, 0x7c53b6b0, 0x7110a069, 0x75d1adde,
|
0x6b968ddb, 0x6f57806c, 0x621496b5, 0x66d59b02, 0x5e9ad6bf, 0x5a5bdb08,
|
0x5718cdd1, 0x53d9c066, 0x4d9ee063, 0x495fedd4, 0x441cfb0d, 0x40ddf6ba,
|
0xaca3d697, 0xa862db20, 0xa521cdf9, 0xa1e0c04e, 0xbfa7e04b, 0xbb66edfc,
|
0xb625fb25, 0xb2e4f692, 0x8aabbb2f, 0x8e6ab698, 0x8329a041, 0x87e8adf6,
|
0x99af8df3, 0x9d6e8044, 0x902d969d, 0x94ec9b2a, 0xe0b30de7, 0xe4720050,
|
0xe9311689, 0xedf01b3e, 0xf3b73b3b, 0xf776368c, 0xfa352055, 0xfef42de2,
|
0xc6bb605f, 0xc27a6de8, 0xcf397b31, 0xcbf87686, 0xd5bf5683, 0xd17e5b34,
|
0xdc3d4ded, 0xd8fc405a, 0x6904c0ee, 0x6dc5cd59, 0x6086db80, 0x6447d637,
|
0x7a00f632, 0x7ec1fb85, 0x7382ed5c, 0x7743e0eb, 0x4f0cad56, 0x4bcda0e1,
|
0x468eb638, 0x424fbb8f, 0x5c089b8a, 0x58c9963d, 0x558a80e4, 0x514b8d53,
|
0x25141b9e, 0x21d51629, 0x2c9600f0, 0x28570d47, 0x36102d42, 0x32d120f5,
|
0x3f92362c, 0x3b533b9b, 0x031c7626, 0x07dd7b91, 0x0a9e6d48, 0x0e5f60ff,
|
0x101840fa, 0x14d94d4d, 0x199a5b94, 0x1d5b5623, 0xf125760e, 0xf5e47bb9,
|
0xf8a76d60, 0xfc6660d7, 0xe22140d2, 0xe6e04d65, 0xeba35bbc, 0xef62560b,
|
0xd72d1bb6, 0xd3ec1601, 0xdeaf00d8, 0xda6e0d6f, 0xc4292d6a, 0xc0e820dd,
|
0xcdab3604, 0xc96a3bb3, 0xbd35ad7e, 0xb9f4a0c9, 0xb4b7b610, 0xb076bba7,
|
0xae319ba2, 0xaaf09615, 0xa7b380cc, 0xa3728d7b, 0x9b3dc0c6, 0x9ffccd71,
|
0x92bfdba8, 0x967ed61f, 0x8839f61a, 0x8cf8fbad, 0x81bbed74, 0x857ae0c3,
|
0x5d86a099, 0x5947ad2e, 0x5404bbf7, 0x50c5b640, 0x4e829645, 0x4a439bf2,
|
0x47008d2b, 0x43c1809c, 0x7b8ecd21, 0x7f4fc096, 0x720cd64f, 0x76cddbf8,
|
0x688afbfd, 0x6c4bf64a, 0x6108e093, 0x65c9ed24, 0x11967be9, 0x1557765e,
|
0x18146087, 0x1cd56d30, 0x02924d35, 0x06534082, 0x0b10565b, 0x0fd15bec,
|
0x379e1651, 0x335f1be6, 0x3e1c0d3f, 0x3add0088, 0x249a208d, 0x205b2d3a,
|
0x2d183be3, 0x29d93654, 0xc5a71679, 0xc1661bce, 0xcc250d17, 0xc8e400a0,
|
0xd6a320a5, 0xd2622d12, 0xdf213bcb, 0xdbe0367c, 0xe3af7bc1, 0xe76e7676,
|
0xea2d60af, 0xeeec6d18, 0xf0ab4d1d, 0xf46a40aa, 0xf9295673, 0xfde85bc4,
|
0x89b7cd09, 0x8d76c0be, 0x8035d667, 0x84f4dbd0, 0x9ab3fbd5, 0x9e72f662,
|
0x9331e0bb, 0x97f0ed0c, 0xafbfa0b1, 0xab7ead06, 0xa63dbbdf, 0xa2fcb668,
|
0xbcbb966d, 0xb87a9bda, 0xb5398d03, 0xb1f880b4,
|
};
|
|
uint32_t CRC_32(uint8_t *pData, uint32_t ulSize)
|
{
|
uint32_t i;
|
uint32_t nAccum = 0;
|
for (i = 0; i < ulSize; i++) {
|
nAccum = (nAccum << 8) ^ gTable_Crc32[(nAccum >> 24) ^ (*pData++)];
|
}
|
return nAccum;
|
}
|
|
void P_RC4(uint8_t *buf, uint32_t len)
|
{
|
uint8_t S[256], K[256], temp;
|
uint32_t i, j, t, x;
|
uint8_t key[16] = { 124, 78, 3, 4, 85, 5, 9, 7, 45, 44, 123, 56, 23, 13, 23,
|
17
|
};
|
|
j = 0;
|
for (i = 0; i < 256; i++) {
|
S[i] = (uint8_t) i;
|
j &= 0x0f;
|
K[i] = key[j];
|
j++;
|
}
|
|
j = 0;
|
for (i = 0; i < 256; i++) {
|
j = (j + S[i] + K[i]) % 256;
|
temp = S[i];
|
S[i] = S[j];
|
S[j] = temp;
|
}
|
|
i = j = 0;
|
for (x = 0; x < len; x++) {
|
i = (i + 1) % 256;
|
j = (j + S[i]) % 256;
|
temp = S[i];
|
S[i] = S[j];
|
S[j] = temp;
|
t = (S[i] + (S[j] % 256)) % 256;
|
buf[x] = buf[x] ^ S[t];
|
}
|
}
|
|
static inline void fixPath(char *path)
|
{
|
int i, len = strlen(path);
|
char tmp[MAX_LINE_LEN];
|
char *start, *end;
|
|
for (i = 0; i < len; i++) {
|
if (path[i] == '\\')
|
path[i] = '/';
|
else if (path[i] == '\r' || path[i] == '\n')
|
path[i] = '\0';
|
}
|
|
if (strlen(gLegacyPath) && strlen(gNewPath)) {
|
start = strstr(path, gLegacyPath);
|
if (start) {
|
end = start + strlen(gLegacyPath);
|
/* Backup, so tmp can be src for strcat() */
|
strcpy(tmp, end);
|
/* Terminate, so path can be dest for strcat() */
|
*start = '\0';
|
strcat(path, gNewPath);
|
strcat(path, tmp);
|
} else {
|
strcpy(tmp, path);
|
strcpy(path, gNewPath);
|
strcat(path, tmp);
|
}
|
} else if ((ulong)path != (ulong)gOpts.outPath && /* ignore output */
|
gPrePath && strncmp(path, gPrePath, strlen(gPrePath))) {
|
strcpy(tmp, path);
|
strcpy(path, gPrePath);
|
strcat(path, tmp);
|
}
|
}
|
|
static bool parseChip(FILE *file)
|
{
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_NAME "=%s", gOpts.chip) != 1) {
|
return false;
|
}
|
LOGD("chip:%s\n", gOpts.chip);
|
return true;
|
}
|
|
static bool parseVersion(FILE *file)
|
{
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_MAJOR "=%d", &gOpts.major) != 1)
|
return false;
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_MINOR "=%d", &gOpts.minor) != 1)
|
return false;
|
LOGD("major:%d, minor:%d\n", gOpts.major, gOpts.minor);
|
return true;
|
}
|
|
static bool parse471(FILE *file)
|
{
|
int i, index, pos;
|
char buf[MAX_LINE_LEN];
|
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_NUM "=%d", &gOpts.code471Num) != 1)
|
return false;
|
LOGD("num:%d\n", gOpts.code471Num);
|
if (!gOpts.code471Num)
|
return true;
|
if (gOpts.code471Num < 0)
|
return false;
|
gOpts.code471Path = (line_t *)malloc(sizeof(line_t) * gOpts.code471Num);
|
for (i = 0; i < gOpts.code471Num; i++) {
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_PATH "%d=%[^\r^\n]", &index, buf) != 2)
|
return false;
|
index--;
|
fixPath(buf);
|
strcpy((char *)gOpts.code471Path[index], buf);
|
LOGD("path%i:%s\n", index, gOpts.code471Path[index]);
|
}
|
pos = ftell(file);
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_SLEEP "=%d", &gOpts.code471Sleep) != 1)
|
fseek(file, pos, SEEK_SET);
|
LOGD("sleep:%d\n", gOpts.code471Sleep);
|
return true;
|
}
|
|
static bool parse472(FILE *file)
|
{
|
int i, index, pos;
|
char buf[MAX_LINE_LEN];
|
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_NUM "=%d", &gOpts.code472Num) != 1)
|
return false;
|
LOGD("num:%d\n", gOpts.code472Num);
|
if (!gOpts.code472Num)
|
return true;
|
if (gOpts.code472Num < 0)
|
return false;
|
gOpts.code472Path = (line_t *)malloc(sizeof(line_t) * gOpts.code472Num);
|
for (i = 0; i < gOpts.code472Num; i++) {
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_PATH "%d=%[^\r^\n]", &index, buf) != 2)
|
return false;
|
fixPath(buf);
|
index--;
|
strcpy((char *)gOpts.code472Path[index], buf);
|
LOGD("path%i:%s\n", index, gOpts.code472Path[index]);
|
}
|
pos = ftell(file);
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_SLEEP "=%d", &gOpts.code472Sleep) != 1)
|
fseek(file, pos, SEEK_SET);
|
LOGD("sleep:%d\n", gOpts.code472Sleep);
|
return true;
|
}
|
|
static bool parseLoader(FILE *file)
|
{
|
int i, j, index, pos;
|
char buf[MAX_LINE_LEN];
|
char buf2[MAX_LINE_LEN];
|
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
pos = ftell(file);
|
if (fscanf(file, OPT_NUM "=%d", &gOpts.loaderNum) != 1) {
|
fseek(file, pos, SEEK_SET);
|
if (fscanf(file, OPT_LOADER_NUM "=%d", &gOpts.loaderNum) != 1) {
|
return false;
|
}
|
}
|
LOGD("num:%d\n", gOpts.loaderNum);
|
if (!gOpts.loaderNum)
|
return false;
|
if (gOpts.loaderNum < 0)
|
return false;
|
gOpts.loader = (name_entry *)malloc(sizeof(name_entry) * gOpts.loaderNum);
|
for (i = 0; i < gOpts.loaderNum; i++) {
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_LOADER_NAME "%d=%s", &index, buf) != 2)
|
return false;
|
index--;
|
strcpy(gOpts.loader[index].name, buf);
|
LOGD("name%d:%s\n", index, gOpts.loader[index].name);
|
}
|
for (i = 0; i < gOpts.loaderNum; i++) {
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, "%[^=]=%[^\r^\n]", buf, buf2) != 2)
|
return false;
|
for (j = 0; j < gOpts.loaderNum; j++) {
|
if (!strcmp(gOpts.loader[j].name, buf)) {
|
fixPath(buf2);
|
strcpy(gOpts.loader[j].path, buf2);
|
LOGD("%s=%s\n", gOpts.loader[j].name, gOpts.loader[j].path);
|
break;
|
}
|
}
|
if (j >= gOpts.loaderNum) {
|
return false;
|
}
|
}
|
return true;
|
}
|
|
static bool parseOut(FILE *file)
|
{
|
if (SCANF_EAT(file) != 0) {
|
return false;
|
}
|
if (fscanf(file, OPT_OUT_PATH "=%[^\r^\n]", gOpts.outPath) != 1)
|
return false;
|
/* fixPath(gOpts.outPath); */
|
printf("out:%s\n", gOpts.outPath);
|
return true;
|
}
|
|
void printOpts(FILE *out)
|
{
|
uint32_t i;
|
fprintf(out, SEC_CHIP "\n" OPT_NAME "=%s\n", gOpts.chip);
|
fprintf(out, SEC_VERSION "\n" OPT_MAJOR "=%d\n" OPT_MINOR "=%d\n",
|
gOpts.major, gOpts.minor);
|
|
fprintf(out, SEC_471 "\n" OPT_NUM "=%d\n", gOpts.code471Num);
|
for (i = 0; i < gOpts.code471Num; i++) {
|
fprintf(out, OPT_PATH "%d=%s\n", i + 1, gOpts.code471Path[i]);
|
}
|
if (gOpts.code471Sleep > 0)
|
fprintf(out, OPT_SLEEP "=%d\n", gOpts.code471Sleep);
|
|
fprintf(out, SEC_472 "\n" OPT_NUM "=%d\n", gOpts.code472Num);
|
for (i = 0; i < gOpts.code472Num; i++) {
|
fprintf(out, OPT_PATH "%d=%s\n", i + 1, gOpts.code472Path[i]);
|
}
|
if (gOpts.code472Sleep > 0)
|
fprintf(out, OPT_SLEEP "=%d\n", gOpts.code472Sleep);
|
|
fprintf(out, SEC_LOADER "\n" OPT_NUM "=%d\n", gOpts.loaderNum);
|
for (i = 0; i < gOpts.loaderNum; i++) {
|
fprintf(out, OPT_LOADER_NAME "%d=%s\n", i + 1, gOpts.loader[i].name);
|
}
|
for (i = 0; i < gOpts.loaderNum; i++) {
|
fprintf(out, "%s=%s\n", gOpts.loader[i].name, gOpts.loader[i].path);
|
}
|
|
fprintf(out, SEC_OUT "\n" OPT_OUT_PATH "=%s\n", gOpts.outPath);
|
}
|
|
static bool parseOpts_from_file(void)
|
{
|
bool ret = false;
|
bool chipOk = false;
|
bool versionOk = false;
|
bool code471Ok = true;
|
bool code472Ok = true;
|
bool loaderOk = false;
|
bool outOk = false;
|
char buf[MAX_LINE_LEN];
|
|
char *configPath = (gConfigPath == NULL) ? DEF_CONFIG_FILE : gConfigPath;
|
FILE *file;
|
file = fopen(configPath, "r");
|
if (!file) {
|
fprintf(stderr, "config(%s) not found!\n", configPath);
|
if (configPath == (char *)DEF_CONFIG_FILE) {
|
file = fopen(DEF_CONFIG_FILE, "w");
|
if (file) {
|
fprintf(stderr, "create defconfig\n");
|
printOpts(file);
|
}
|
}
|
goto end;
|
}
|
|
LOGD("start parse\n");
|
|
if (SCANF_EAT(file) != 0) {
|
goto end;
|
}
|
while (fscanf(file, "%s", buf) == 1) {
|
if (!strcmp(buf, SEC_CHIP)) {
|
chipOk = parseChip(file);
|
if (!chipOk) {
|
LOGE("parseChip failed!\n");
|
goto end;
|
}
|
} else if (!strcmp(buf, SEC_VERSION)) {
|
versionOk = parseVersion(file);
|
if (!versionOk) {
|
LOGE("parseVersion failed!\n");
|
goto end;
|
}
|
} else if (!strcmp(buf, SEC_471)) {
|
code471Ok = parse471(file);
|
if (!code471Ok) {
|
LOGE("parse471 failed!\n");
|
goto end;
|
}
|
} else if (!strcmp(buf, SEC_472)) {
|
code472Ok = parse472(file);
|
if (!code472Ok) {
|
LOGE("parse472 failed!\n");
|
goto end;
|
}
|
} else if (!strcmp(buf, SEC_LOADER)) {
|
loaderOk = parseLoader(file);
|
if (!loaderOk) {
|
LOGE("parseLoader failed!\n");
|
goto end;
|
}
|
} else if (!strcmp(buf, SEC_OUT)) {
|
outOk = parseOut(file);
|
if (!outOk) {
|
LOGE("parseOut failed!\n");
|
goto end;
|
}
|
} else if (buf[0] == '#') {
|
continue;
|
} else {
|
LOGE("unknown sec: %s!\n", buf);
|
goto end;
|
}
|
if (SCANF_EAT(file) != 0) {
|
goto end;
|
}
|
}
|
|
if (chipOk && versionOk && code471Ok && code472Ok && loaderOk && outOk)
|
ret = true;
|
end:
|
if (file)
|
fclose(file);
|
return ret;
|
}
|
|
static bool parseOpts_from_cmdline(int argc, char **argv)
|
{
|
int i;
|
int tag = 0;
|
int v0, v1, v2, v3;
|
|
for (i = 2; i < argc; i++) {
|
if (!strcmp(OPT_471, argv[i])) {
|
i++;
|
snprintf(gOpts.code471Path[0], sizeof(gOpts.code471Path[0]), "%s",
|
argv[i]);
|
tag |= 1;
|
} else if (!strcmp(OPT_472, argv[i])) {
|
i++;
|
snprintf(gOpts.code472Path[0], sizeof(gOpts.code472Path[0]), "%s",
|
argv[i]);
|
tag |= 2;
|
} else if (!strcmp(OPT_DATA, argv[i])) {
|
i++;
|
snprintf(gOpts.loader[0].path, sizeof(gOpts.loader[0].path), "%s",
|
argv[i]);
|
tag |= 4;
|
} else if (!strcmp(OPT_BOOT, argv[i])) {
|
i++;
|
snprintf(gOpts.loader[1].path, sizeof(gOpts.loader[1].path), "%s",
|
argv[i]);
|
tag |= 8;
|
} else if (!strcmp(OPT_OUT, argv[i])) {
|
i++;
|
snprintf(gOpts.outPath, sizeof(gOpts.outPath), "%s", argv[i]);
|
tag |= 0x10;
|
} else if (!strcmp(OPT_CHIP, argv[i])) {
|
i++;
|
snprintf(gOpts.chip, sizeof(gOpts.chip), "%s", argv[i]);
|
tag |= 0x20;
|
} else if (!strcmp(OPT_VERSION, argv[i])) {
|
}
|
}
|
|
sscanf(gOpts.loader[0].path, "%*[^v]v%d.%d.bin", &v0, &v1);
|
sscanf(gOpts.loader[1].path, "%*[^v]v%d.%d.bin", &v2, &v3);
|
gOpts.major = v2;
|
gOpts.minor = v3;
|
snprintf(gOpts.outPath, sizeof(gOpts.outPath),
|
"%s_loader_v%d.%02d.%d%02d.bin", gOpts.chip, v0, v1, v2, v3);
|
return ((tag & 0x0f) == 0x0f) ? true : false;
|
}
|
|
bool initOpts(int argc, char **argv)
|
{
|
bool ret;
|
|
/* set default opts */
|
gOpts.major = DEF_MAJOR;
|
gOpts.minor = DEF_MINOR;
|
strcpy(gOpts.chip, DEF_CHIP);
|
gOpts.code471Sleep = DEF_CODE471_SLEEP;
|
gOpts.code472Sleep = DEF_CODE472_SLEEP;
|
gOpts.code471Num = DEF_CODE471_NUM;
|
gOpts.code471Path = (line_t *)malloc(sizeof(line_t) * gOpts.code471Num);
|
strcpy((char *)gOpts.code471Path[0], DEF_CODE471_PATH);
|
gOpts.code472Num = DEF_CODE472_NUM;
|
gOpts.code472Path = (line_t *)malloc(sizeof(line_t) * gOpts.code472Num);
|
strcpy((char *)gOpts.code472Path[0], DEF_CODE472_PATH);
|
gOpts.loaderNum = DEF_LOADER_NUM;
|
gOpts.loader = (name_entry *)malloc(sizeof(name_entry) * gOpts.loaderNum);
|
strcpy(gOpts.loader[0].name, DEF_LOADER0);
|
strcpy(gOpts.loader[0].path, DEF_LOADER0_PATH);
|
strcpy(gOpts.loader[1].name, DEF_LOADER1);
|
strcpy(gOpts.loader[1].path, DEF_LOADER1_PATH);
|
strcpy(gOpts.outPath, DEF_OUT_PATH);
|
|
if (argc > 10)
|
ret = parseOpts_from_cmdline(argc, argv);
|
else
|
ret = parseOpts_from_file();
|
|
return ret;
|
}
|
|
/************merge code****************/
|
|
static inline uint32_t getBCD(unsigned short value)
|
{
|
uint8_t tmp[2] = { 0 };
|
int i;
|
uint32_t ret;
|
if (value > 0xFFFF) {
|
return 0;
|
}
|
for (i = 0; i < 2; i++) {
|
tmp[i] = (((value / 10) % 10) << 4) | (value % 10);
|
value /= 100;
|
}
|
ret = ((uint16_t)(tmp[1] << 8)) | tmp[0];
|
|
LOGD("ret:%x\n", ret);
|
return ret & 0xFF;
|
}
|
|
static inline void str2wide(const char *str, uint16_t *wide, int len)
|
{
|
int i;
|
for (i = 0; i < len; i++) {
|
wide[i] = (uint16_t) str[i];
|
}
|
wide[len] = 0;
|
}
|
|
static inline void getName(char *path, uint16_t *dst)
|
{
|
char *end;
|
char *start;
|
int len;
|
if (!path || !dst)
|
return;
|
start = strrchr(path, '/');
|
if (!start)
|
start = path;
|
else
|
start++;
|
end = strrchr(path, '.');
|
if (!end)
|
end = path + strlen(path);
|
len = end - start;
|
if (len >= MAX_NAME_LEN)
|
len = MAX_NAME_LEN - 1;
|
str2wide(start, dst, len);
|
|
if (gDebug) {
|
char name[MAX_NAME_LEN];
|
memset(name, 0, sizeof(name));
|
memcpy(name, start, len);
|
LOGD("path:%s, name:%s\n", path, name);
|
}
|
}
|
|
static inline bool getFileSize(const char *path, uint32_t *size)
|
{
|
struct stat st;
|
if (stat(path, &st) < 0)
|
return false;
|
*size = st.st_size;
|
LOGD("path:%s, size:%d\n", path, *size);
|
return true;
|
}
|
|
static inline rk_time getTime(void)
|
{
|
rk_time rkTime;
|
|
struct tm *tm;
|
time_t tt = time(NULL);
|
tm = localtime(&tt);
|
rkTime.year = tm->tm_year + 1900;
|
rkTime.month = tm->tm_mon + 1;
|
rkTime.day = tm->tm_mday;
|
rkTime.hour = tm->tm_hour;
|
rkTime.minute = tm->tm_min;
|
rkTime.second = tm->tm_sec;
|
LOGD("%d-%d-%d %02d:%02d:%02d\n", rkTime.year, rkTime.month, rkTime.day,
|
rkTime.hour, rkTime.minute, rkTime.second);
|
return rkTime;
|
}
|
|
static bool writeFile(FILE *outFile, const char *path, bool fix)
|
{
|
bool ret = false;
|
uint32_t size = 0, fixSize = 0;
|
uint8_t *buf;
|
|
FILE *inFile = fopen(path, "rb");
|
if (!inFile)
|
goto end;
|
|
if (!getFileSize(path, &size))
|
goto end;
|
if (fix) {
|
fixSize = ((size - 1) / SMALL_PACKET + 1) * SMALL_PACKET;
|
uint32_t tmp = fixSize % ENTRY_ALIGN;
|
tmp = tmp ? (ENTRY_ALIGN - tmp) : 0;
|
fixSize += tmp;
|
memset(gBuf, 0, fixSize);
|
} else {
|
memset(gBuf, 0, size + ENTRY_ALIGN);
|
}
|
if (!fread(gBuf, size, 1, inFile))
|
goto end;
|
|
if (fix) {
|
|
buf = gBuf;
|
size = fixSize;
|
while (1) {
|
P_RC4(buf, fixSize < SMALL_PACKET ? fixSize : SMALL_PACKET);
|
buf += SMALL_PACKET;
|
if (fixSize <= SMALL_PACKET)
|
break;
|
fixSize -= SMALL_PACKET;
|
}
|
} else {
|
uint32_t tmp = size % ENTRY_ALIGN;
|
tmp = tmp ? (ENTRY_ALIGN - tmp) : 0;
|
size += tmp;
|
P_RC4(gBuf, size);
|
}
|
|
if (!fwrite(gBuf, size, 1, outFile))
|
goto end;
|
ret = true;
|
end:
|
if (inFile)
|
fclose(inFile);
|
if (!ret)
|
LOGE("write entry(%s) failed\n", path);
|
return ret;
|
}
|
|
static bool saveEntry(FILE *outFile, char *path, rk_entry_type type,
|
uint16_t delay, uint32_t *offset, char *fixName,
|
bool fix)
|
{
|
LOGD("write:%s\n", path);
|
uint32_t size;
|
rk_boot_entry entry;
|
memset(&entry, 0, sizeof(rk_boot_entry));
|
|
LOGD("write:%s\n", path);
|
|
getName(fixName ? fixName : path, entry.name);
|
entry.size = sizeof(rk_boot_entry);
|
entry.type = type;
|
entry.dataOffset = *offset;
|
if (!getFileSize(path, &size)) {
|
LOGE("save entry(%s) failed:\n\tcannot get file size.\n", path);
|
return false;
|
}
|
if (fix)
|
size = ((size - 1) / SMALL_PACKET + 1) * SMALL_PACKET;
|
uint32_t tmp = size % ENTRY_ALIGN;
|
size += tmp ? (ENTRY_ALIGN - tmp) : 0;
|
LOGD("align size:%d\n", size);
|
entry.dataSize = size;
|
entry.dataDelay = delay;
|
*offset += size;
|
fwrite(&entry, sizeof(rk_boot_entry), 1, outFile);
|
return true;
|
}
|
|
static inline uint32_t convertChipType(const char *chip)
|
{
|
char buffer[5];
|
memset(buffer, 0, sizeof(buffer));
|
snprintf(buffer, sizeof(buffer), "%s", chip);
|
return buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
|
}
|
|
static inline uint32_t getChipType(const char *chip)
|
{
|
LOGD("chip:%s\n", chip);
|
int chipType = RKNONE_DEVICE;
|
if (!chip) {
|
goto end;
|
}
|
if (!strcmp(chip, CHIP_RK28)) {
|
chipType = RK28_DEVICE;
|
} else if (!strcmp(chip, CHIP_RK28)) {
|
chipType = RK28_DEVICE;
|
} else if (!strcmp(chip, CHIP_RK281X)) {
|
chipType = RK281X_DEVICE;
|
} else if (!strcmp(chip, CHIP_RKPANDA)) {
|
chipType = RKPANDA_DEVICE;
|
} else if (!strcmp(chip, CHIP_RK27)) {
|
chipType = RK27_DEVICE;
|
} else if (!strcmp(chip, CHIP_RKNANO)) {
|
chipType = RKNANO_DEVICE;
|
} else if (!strcmp(chip, CHIP_RKSMART)) {
|
chipType = RKSMART_DEVICE;
|
} else if (!strcmp(chip, CHIP_RKCROWN)) {
|
chipType = RKCROWN_DEVICE;
|
} else if (!strcmp(chip, CHIP_RKCAYMAN)) {
|
chipType = RKCAYMAN_DEVICE;
|
} else if (!strcmp(chip, CHIP_RK29)) {
|
chipType = RK29_DEVICE;
|
} else if (!strcmp(chip, CHIP_RK292X)) {
|
chipType = RK292X_DEVICE;
|
} else if (!strcmp(chip, CHIP_RK30)) {
|
chipType = RK30_DEVICE;
|
} else if (!strcmp(chip, CHIP_RK30B)) {
|
chipType = RK30B_DEVICE;
|
} else if (!strcmp(chip, CHIP_RK31)) {
|
chipType = RK31_DEVICE;
|
} else if (!strcmp(chip, CHIP_RK32)) {
|
chipType = RK32_DEVICE;
|
} else {
|
chipType = convertChipType(chip + 2);
|
}
|
|
end:
|
LOGD("type:0x%x\n", chipType);
|
if (chipType == RKNONE_DEVICE) {
|
LOGE("chip type not support!\n");
|
}
|
return chipType;
|
}
|
|
static inline void getBoothdr(rk_boot_header *hdr)
|
{
|
memset(hdr, 0, sizeof(rk_boot_header));
|
hdr->tag = TAG;
|
hdr->size = sizeof(rk_boot_header);
|
hdr->version = (getBCD(gOpts.major) << 8) | getBCD(gOpts.minor);
|
hdr->mergerVersion = MERGER_VERSION;
|
hdr->releaseTime = getTime();
|
hdr->chipType = getChipType(gOpts.chip);
|
|
hdr->code471Num = gOpts.code471Num;
|
hdr->code471Offset = sizeof(rk_boot_header);
|
hdr->code471Size = sizeof(rk_boot_entry);
|
|
hdr->code472Num = gOpts.code472Num;
|
hdr->code472Offset = hdr->code471Offset + gOpts.code471Num * hdr->code471Size;
|
hdr->code472Size = sizeof(rk_boot_entry);
|
|
hdr->loaderNum = gOpts.loaderNum;
|
hdr->loaderOffset = hdr->code472Offset + gOpts.code472Num * hdr->code472Size;
|
hdr->loaderSize = sizeof(rk_boot_entry);
|
if (!enableRC4)
|
hdr->rc4Flag = 1;
|
}
|
|
static inline uint32_t getCrc(const char *path)
|
{
|
uint32_t size = 0;
|
uint32_t crc = 0;
|
FILE *file = fopen(path, "rb");
|
getFileSize(path, &size);
|
if (!file)
|
goto end;
|
if (!fread(gBuf, size, 1, file))
|
goto end;
|
crc = CRC_32(gBuf, size);
|
LOGD("crc:0x%08x\n", crc);
|
end:
|
if (file)
|
fclose(file);
|
return crc;
|
}
|
|
static bool mergeBoot(int argc, char **argv)
|
{
|
uint32_t dataOffset;
|
bool ret = false;
|
int i;
|
FILE *outFile;
|
uint32_t crc;
|
rk_boot_header hdr;
|
|
if (!initOpts(argc, argv))
|
return false;
|
{
|
char *subfix = strstr(gOpts.outPath, OUT_SUBFIX);
|
char version[MAX_LINE_LEN];
|
snprintf(version, sizeof(version), "%s", gSubfix);
|
if (subfix && !strcmp(subfix, OUT_SUBFIX)) {
|
subfix[0] = '\0';
|
}
|
strcat(gOpts.outPath, version);
|
printf("fix opt:%s\n", gOpts.outPath);
|
}
|
|
if (gDebug) {
|
printf("---------------\nUSING CONFIG:\n");
|
printOpts(stdout);
|
printf("---------------\n\n");
|
}
|
|
outFile = fopen(gOpts.outPath, "wb+");
|
if (!outFile) {
|
LOGE("open out file(%s) failed\n", gOpts.outPath);
|
goto end;
|
}
|
|
getBoothdr(&hdr);
|
LOGD("write hdr\n");
|
fwrite(&hdr, 1, sizeof(rk_boot_header), outFile);
|
|
dataOffset = sizeof(rk_boot_header) +
|
(gOpts.code471Num + gOpts.code472Num + gOpts.loaderNum) *
|
sizeof(rk_boot_entry);
|
|
LOGD("write code 471 entry\n");
|
for (i = 0; i < gOpts.code471Num; i++) {
|
if (!saveEntry(outFile, (char *)gOpts.code471Path[i], ENTRY_471,
|
gOpts.code471Sleep, &dataOffset, NULL, false))
|
goto end;
|
}
|
LOGD("write code 472 entry\n");
|
for (i = 0; i < gOpts.code472Num; i++) {
|
if (!saveEntry(outFile, (char *)gOpts.code472Path[i], ENTRY_472,
|
gOpts.code472Sleep, &dataOffset, NULL, false))
|
goto end;
|
}
|
LOGD("write loader entry\n");
|
for (i = 0; i < gOpts.loaderNum; i++) {
|
if (!saveEntry(outFile, gOpts.loader[i].path, ENTRY_LOADER, 0, &dataOffset,
|
gOpts.loader[i].name, true))
|
goto end;
|
}
|
|
LOGD("write code 471\n");
|
for (i = 0; i < gOpts.code471Num; i++) {
|
if (!writeFile(outFile, (char *)gOpts.code471Path[i], false))
|
goto end;
|
}
|
LOGD("write code 472\n");
|
for (i = 0; i < gOpts.code472Num; i++) {
|
if (!writeFile(outFile, (char *)gOpts.code472Path[i], false))
|
goto end;
|
}
|
LOGD("write loader\n");
|
for (i = 0; i < gOpts.loaderNum; i++) {
|
if (!writeFile(outFile, gOpts.loader[i].path, true))
|
goto end;
|
}
|
fflush(outFile);
|
|
LOGD("write crc\n");
|
crc = getCrc(gOpts.outPath);
|
if (!fwrite(&crc, sizeof(crc), 1, outFile))
|
goto end;
|
|
ret = true;
|
end:
|
if (outFile)
|
fclose(outFile);
|
return ret;
|
}
|
|
/************merge code end************/
|
/************unpack code***************/
|
|
static inline void wide2str(const uint16_t *wide, char *str, int len)
|
{
|
int i;
|
for (i = 0; i < len; i++) {
|
str[i] = (char)(wide[i] & 0xFF);
|
}
|
str[len] = 0;
|
}
|
|
static bool unpackEntry(rk_boot_entry *entry, const char *name, FILE *inFile)
|
{
|
bool ret = false;
|
int size, i;
|
FILE *outFile = fopen(name, "wb+");
|
if (!outFile)
|
goto end;
|
printf("unpack entry(%s)\n", name);
|
fseek(inFile, entry->dataOffset, SEEK_SET);
|
size = entry->dataSize;
|
if (!fread(gBuf, size, 1, inFile))
|
goto end;
|
if (entry->type == ENTRY_LOADER) {
|
for (i = 0; i < size / SMALL_PACKET; i++)
|
P_RC4(gBuf + i * SMALL_PACKET, SMALL_PACKET);
|
if (size % SMALL_PACKET) {
|
P_RC4(gBuf + i * SMALL_PACKET, size - SMALL_PACKET * 512);
|
}
|
} else {
|
P_RC4(gBuf, size);
|
}
|
if (!fwrite(gBuf, size, 1, outFile))
|
goto end;
|
ret = true;
|
end:
|
if (outFile)
|
fclose(outFile);
|
return ret;
|
}
|
|
static bool unpackBoot(char *path)
|
{
|
bool ret = false;
|
FILE *inFile = fopen(path, "rb");
|
int entryNum, i;
|
char name[MAX_NAME_LEN];
|
rk_boot_entry *entrys;
|
if (!inFile) {
|
fprintf(stderr, "loader(%s) not found\n", path);
|
goto end;
|
}
|
|
rk_boot_header hdr;
|
if (!fread(&hdr, sizeof(rk_boot_header), 1, inFile)) {
|
fprintf(stderr, "read header failed\n");
|
goto end;
|
}
|
|
entryNum = hdr.code471Num + hdr.code472Num + hdr.loaderNum;
|
entrys = (rk_boot_entry *)malloc(sizeof(rk_boot_entry) * entryNum);
|
if (!fread(entrys, sizeof(rk_boot_entry) * entryNum, 1, inFile)) {
|
fprintf(stderr, "read data failed\n");
|
goto end;
|
}
|
|
LOGD("entry num:%d\n", entryNum);
|
for (i = 0; i < entryNum; i++) {
|
wide2str(entrys[i].name, name, MAX_NAME_LEN);
|
|
LOGD("entry:t=%d, name=%s, off=%d, size=%d\n", entrys[i].type, name,
|
entrys[i].dataOffset, entrys[i].dataSize);
|
if (!unpackEntry(entrys + i, name, inFile)) {
|
fprintf(stderr, "unpack entry(%s) failed\n", name);
|
goto end;
|
}
|
}
|
|
ret = true;
|
end:
|
if (inFile)
|
fclose(inFile);
|
return ret;
|
}
|
|
/************unpack code end***********/
|
|
static void printHelp(void)
|
{
|
printf("Usage1: boot_merger [options]... FILE\n");
|
printf("Merge or unpack Rockchip's loader (Default action is to merge.)\n");
|
printf("Options:\n");
|
printf("\t" OPT_MERGE "\t\t\tMerge loader with specified config.\n");
|
printf("\t" OPT_UNPACK "\t\tUnpack specified loader to current dir.\n");
|
printf("\t" OPT_VERBOSE "\t\tDisplay more runtime informations.\n");
|
printf("\t" OPT_HELP "\t\t\tDisplay this information.\n");
|
printf("\t" OPT_VERSION "\t\tDisplay version information.\n");
|
printf("\t" OPT_SUBFIX "\t\tSpec subfix.\n");
|
printf("\t" OPT_REPLACE "\t\tReplace some part of binary path.\n");
|
printf("\t" OPT_PREPATH "\t\tAdd prefix path of binary path.\n");
|
printf("\t" OPT_SIZE
|
"\t\tImage size.\"--size [image KB size]\", must be 512KB aligned\n");
|
printf("Usage2: boot_merger [options] [parameter]\n");
|
printf("All below five option are must in this mode!\n");
|
printf("\t" OPT_CHIP "\t\tChip type, used for check with usbplug.\n");
|
printf("\t" OPT_471 "\t\t471 for download, ddr.bin.\n");
|
printf("\t" OPT_472 "\t\t472 for download, usbplug.bin.\n");
|
printf("\t" OPT_DATA "\t\tloader0 for flash, ddr.bin.\n");
|
printf("\t" OPT_BOOT "\t\tloader1 for flash, miniloader.bin.\n");
|
printf("\n./tools/boot_merger --pack --verbose -c RK322A -1 "
|
"rkbin/rk322x_ddr_300MHz_v1.04.bin -2 "
|
"rkbin/rk32/rk322x_usbplug_v2.32.bin -d "
|
"rkbin/rk32/rk322x_ddr_300MHz_v1.04.bin -b "
|
"rkbin/rk32/rk322x_miniloader_v2.32.bin\n");
|
}
|
|
int main(int argc, char **argv)
|
{
|
|
int i;
|
bool merge = true;
|
char *optPath = NULL;
|
|
for (i = 1; i < argc; i++) {
|
if (!strcmp(OPT_VERBOSE, argv[i])) {
|
gDebug = true;
|
printf("enable debug\n");
|
} else if (!strcmp(OPT_HELP, argv[i])) {
|
printHelp();
|
return 0;
|
} else if (!strcmp(OPT_VERSION, argv[i])) {
|
printf("boot_merger (cjf@rock-chips.com)\t" VERSION "\n");
|
return 0;
|
} else if (!strcmp(OPT_MERGE, argv[i])) {
|
merge = true;
|
} else if (!strcmp(OPT_UNPACK, argv[i])) {
|
merge = false;
|
} else if (!strcmp(OPT_RC4, argv[i])) {
|
printf("enable RC4 for IDB data(both ddr and preloader)\n");
|
enableRC4 = true;
|
} else if (!strcmp(OPT_SUBFIX, argv[i])) {
|
i++;
|
snprintf(gSubfix, sizeof(gSubfix), "%s", argv[i]);
|
} else if (!strcmp(OPT_REPLACE, argv[i])) {
|
i++;
|
snprintf(gLegacyPath, sizeof(gLegacyPath), "%s", argv[i]);
|
i++;
|
snprintf(gNewPath, sizeof(gNewPath), "%s", argv[i]);
|
} else if (!strcmp(OPT_PREPATH, argv[i])) {
|
i++;
|
gPrePath = argv[i];
|
} else if (!strcmp(OPT_SIZE, argv[i])) {
|
g_merge_max_size = strtoul(argv[++i], NULL, 10);
|
if (g_merge_max_size % 512) {
|
printHelp();
|
return -1;
|
}
|
g_merge_max_size *= 1024; /* bytes */
|
} else {
|
optPath = argv[i];
|
break;
|
}
|
}
|
if (!merge && !optPath) {
|
fprintf(stderr, "need set out path to unpack!\n");
|
printHelp();
|
return -1;
|
}
|
|
gBuf = calloc(g_merge_max_size, 1);
|
if (!gBuf) {
|
LOGE("Merge image: calloc buffer error.\n");
|
return -1;
|
}
|
|
if (merge) {
|
LOGD("do_merge\n");
|
gConfigPath = optPath;
|
if (!mergeBoot(argc, argv)) {
|
fprintf(stderr, "merge failed!\n");
|
return -1;
|
}
|
printf("merge success(%s)\n", gOpts.outPath);
|
} else {
|
LOGD("do_unpack\n");
|
if (!unpackBoot(optPath)) {
|
fprintf(stderr, "unpack failed!\n");
|
return -1;
|
}
|
printf("unpack success\n");
|
}
|
return 0;
|
}
|