346 lines
7.2 KiB
C
346 lines
7.2 KiB
C
/**
|
|
@brief merge fdt.app in mtd form a dtb file.\n
|
|
@author Niven Cho
|
|
@ingroup mhdal
|
|
@note Nothing.
|
|
|
|
Copyright Novatek Microelectronics Corp. 2018. All rights reserved.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <kwrap/cmdsys.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <mtd/mtd-user.h>
|
|
#include <sys/ioctl.h>
|
|
#include "libfdt.h"
|
|
|
|
#define CFG_DEBUG_OUTPUT_FILE "/mnt/sd/output.dtb"
|
|
|
|
int chk_file_exist(const char *filename)
|
|
{
|
|
struct stat st;
|
|
if (stat(filename, &st) != 0) {
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int remove_dummy(char *str, int len)
|
|
{
|
|
int i;
|
|
for (i = 0; i < len; i++) {
|
|
if (!isprint(str[i])) {
|
|
str[i] = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int get_partition_phy_size(int fd)
|
|
{
|
|
mtd_info_t meminfo = {0};
|
|
if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
|
|
return -1;
|
|
}
|
|
return (int)meminfo.size;
|
|
}
|
|
|
|
int get_fdtapp_mtd_index(void)
|
|
{
|
|
int i;
|
|
char path[64] = {0};
|
|
char name[32] = {0};
|
|
// get mtd index of fdt.app
|
|
for (i = 0; i < 100; i++) {
|
|
sprintf(path, "/sys/class/mtd/mtd%d/name", i);
|
|
if (chk_file_exist(path) != 0) {
|
|
continue;
|
|
}
|
|
//check lable name
|
|
FILE *fptr = fopen(path, "rt");
|
|
if (fptr == NULL) {
|
|
fprintf(stderr, "unable to open %s.\n", path);
|
|
return -1;
|
|
}
|
|
if (fread(name, 1, sizeof(name), fptr) < 1) {
|
|
fprintf(stderr, "failed to read %s.\n", path);
|
|
fclose(fptr);
|
|
return -1;
|
|
}
|
|
fclose(fptr);
|
|
// because name include '\n'.
|
|
remove_dummy(name, sizeof(name));
|
|
if (strncmp(name, "fdt.app", 8) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int load_dtb_from_rootfs(unsigned char **pp_dtb, int *p_dtb_size, const char *path)
|
|
{
|
|
struct stat st;
|
|
unsigned char *p_dtb = NULL;
|
|
|
|
if (chk_file_exist(path) != 0) {
|
|
fprintf(stderr, "unable to open %s.\n", path);
|
|
return -1;
|
|
} else {
|
|
int fd = open(path, O_RDONLY);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "unable to open %s.\n", path);
|
|
return -1;
|
|
}
|
|
|
|
if (stat(path, &st) != 0) {
|
|
fprintf(stderr, "unable to stat %s\n", path);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
p_dtb = (unsigned char *)malloc(st.st_size);
|
|
if (p_dtb == NULL) {
|
|
fprintf(stderr, "failed to alloc memory %d bytes for %s\n", st.st_size, path);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
if (read(fd, p_dtb, st.st_size) != st.st_size) {
|
|
fprintf(stderr, "unable to read data from %s.\n", path);
|
|
free(p_dtb);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
|
|
if (fdt_check_full(p_dtb, st.st_size) != 0) {
|
|
fprintf(stderr, "invalid fdt on %s.\n", path);
|
|
free(p_dtb);
|
|
return -1;
|
|
}
|
|
}
|
|
*pp_dtb = p_dtb;
|
|
*p_dtb_size = (int)st.st_size;
|
|
return 0;
|
|
}
|
|
|
|
int load_fdt_from_mtd(unsigned char **pp_fdt, int *p_fdt_size, const char *path)
|
|
{
|
|
unsigned char *p_fdt = NULL;
|
|
|
|
if (chk_file_exist(path) != 0) {
|
|
fprintf(stderr, "unable to open %s.\n", path);
|
|
return -1;
|
|
}
|
|
|
|
int fd = open(path, O_RDONLY);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "unable to open %s.\n", path);
|
|
return -1;
|
|
}
|
|
|
|
//got mtd partition size
|
|
int partition_size = get_partition_phy_size(fd);
|
|
if (partition_size < 0) {
|
|
fprintf(stderr, "unable to get partition size from %s.\n", path);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
p_fdt = (unsigned char *)malloc(partition_size);
|
|
if (p_fdt == NULL) {
|
|
fprintf(stderr, "failed to alloc memory %d bytes for %s\n", partition_size, path);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
if (read(fd, p_fdt, partition_size) != partition_size) {
|
|
fprintf(stderr, "unable to read data from %s.\n", path);
|
|
free(p_fdt);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
|
|
//check fdt
|
|
int er = fdt_check_full(p_fdt, partition_size);
|
|
if (er != 0) {
|
|
fprintf(stderr, "invalid fdt on %s.\n", path);
|
|
free(p_fdt);
|
|
return -1;
|
|
}
|
|
|
|
// enlarge the size to write more nodes
|
|
fdt_set_totalsize(p_fdt, partition_size);
|
|
|
|
*pp_fdt = p_fdt;
|
|
*p_fdt_size = partition_size;
|
|
return 0;
|
|
}
|
|
|
|
static int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name)
|
|
{
|
|
int offset;
|
|
|
|
offset = fdt_subnode_offset(fdt, parentoffset, name);
|
|
|
|
if (offset == -FDT_ERR_NOTFOUND) {
|
|
offset = fdt_add_subnode(fdt, parentoffset, name);
|
|
}
|
|
|
|
if (offset < 0) {
|
|
printf("%s: %s: %s\n", __func__, name, fdt_strerror(offset));
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static int merge_node(void *fdt_dst, void *fdt_src, const char *node_name)
|
|
{
|
|
int er;
|
|
int len;
|
|
int child;
|
|
char path[256] = { 0 };
|
|
|
|
if (fdt_dst == NULL || fdt_src == NULL) {
|
|
printf("merge_node: one of fdt is NULL\n");
|
|
return -1;
|
|
}
|
|
|
|
int src_parent = fdt_path_offset(fdt_src, node_name);
|
|
int dst_parent = fdt_path_offset(fdt_dst, node_name);
|
|
|
|
if (dst_parent <= 0) {
|
|
printf("unable to find %s on dst fdt.\n", node_name);
|
|
return -1;
|
|
}
|
|
|
|
if (src_parent <= 0) {
|
|
printf("unable to find %s on src fdt.\n", node_name);
|
|
return -1;
|
|
}
|
|
|
|
fdt_for_each_property_offset(child, fdt_src, src_parent) {
|
|
const struct fdt_property *fdt_prop = fdt_get_property_by_offset(fdt_src, child, &len);
|
|
const char *name = fdt_string(fdt_src, fdt32_to_cpu(fdt_prop->nameoff));
|
|
len = fdt32_to_cpu(fdt_prop->len);
|
|
const void *nodep = fdt_prop->data;
|
|
if ((er = fdt_setprop(fdt_dst, dst_parent, name, nodep, len)) != 0) {
|
|
printf("failed to fdt_setprop %s, er = %d\n", name, er);
|
|
return er;
|
|
}
|
|
}
|
|
|
|
fdt_for_each_subnode(child, fdt_src, src_parent) {
|
|
const char *name = fdt_get_name(fdt_src, child, &len);
|
|
sprintf(path, "%s/%s", node_name, name);
|
|
if (fdt_find_or_add_subnode(fdt_dst, dst_parent, name) <= 0) {
|
|
return -1;
|
|
}
|
|
if ((er = merge_node(fdt_dst, fdt_src, path)) != 0) {
|
|
return er;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int erase_mtd(const char *path, int size)
|
|
{
|
|
int fd = open(path, O_RDWR);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "unable to open %s.\n", path);
|
|
return -1;
|
|
}
|
|
|
|
erase_info_t erase;
|
|
erase.start = 0;
|
|
erase.length = size;
|
|
if (ioctl(fd, MEMERASE, &erase) < 0) {
|
|
fprintf(stderr, "erase failed\n");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
int merge_fdtapp(const char *path_dtb)
|
|
{
|
|
unsigned char *p_dtb = NULL;
|
|
unsigned char *p_fdt = NULL;
|
|
int dtb_size = 0, fdt_size = 0;
|
|
|
|
// load source dtb
|
|
if (load_dtb_from_rootfs(&p_dtb, &dtb_size, path_dtb) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
// get mtd index
|
|
int mtd_idx = get_fdtapp_mtd_index();
|
|
if (mtd_idx < 0) {
|
|
fprintf(stderr, "unable to find fdt.app from mtd.\n");
|
|
free(p_dtb);
|
|
return -1;
|
|
}
|
|
|
|
// load target fdt
|
|
char path_mtd[64] = {0};
|
|
sprintf(path_mtd, "/dev/mtd%d", mtd_idx);
|
|
if (load_fdt_from_mtd(&p_fdt, &fdt_size, path_mtd) != 0) {
|
|
free(p_dtb);
|
|
return -1;
|
|
}
|
|
|
|
// merge
|
|
if (merge_node(p_fdt, p_dtb, "/fastboot") != 0) {
|
|
fprintf(stderr, "failed to merge.\n");
|
|
free(p_fdt);
|
|
free(p_dtb);
|
|
return -1;
|
|
}
|
|
|
|
//make it real size
|
|
fdt_pack(p_fdt);
|
|
|
|
// erase mtd before written
|
|
if (erase_mtd(path_mtd, fdt_size) != 0) {
|
|
free(p_fdt);
|
|
free(p_dtb);
|
|
return -1;
|
|
}
|
|
|
|
// write mtd
|
|
int fd = open(path_mtd, O_RDWR);
|
|
int written = write(fd, p_fdt, fdt_size);
|
|
printf("merge_fdtapp wrote %d bytes into %s\n", written, path_mtd);
|
|
close(fd);
|
|
|
|
#if 0 //write to sd card
|
|
fd = open(CFG_DEBUG_OUTPUT_FILE, O_CREAT | O_WRONLY | O_SYNC);
|
|
write(fd, p_fdt, fdt_totalsize(p_fdt));
|
|
close(fd);
|
|
#endif
|
|
free(p_fdt);
|
|
free(p_dtb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
MAINFUNC_ENTRY(merge_fdtapp, argc, argv)
|
|
{
|
|
#if 0
|
|
return merge_fdtapp("/etc/plugin_hdr.dtb");
|
|
#else
|
|
if (argc < 2) {
|
|
fprintf(stderr, "usage: merge_fdtapp [dtb]\n");
|
|
return -1;
|
|
}
|
|
return merge_fdtapp(argv[1]);
|
|
#endif
|
|
}
|