You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

368 lines
8.5 KiB
C

#include <stdio.h>
#include <libfdt.h>
#include <fdt.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#if 0
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...)
#endif
static int firmware_fdt_read_blob(const char *filename, char **buffp, int *len)
{
int fd = 0; /* assume stdin */
char *buf = NULL;
int ret = 0;
*len = 0;
*buffp = NULL;
if (strcmp(filename, "-") != 0)
{
fd = open(filename, O_RDONLY);
if (fd < 0)
return errno;
}
struct stat st;
stat(filename, &st);
size_t bufsize = st.st_size;
/* Loop until we have read everything */
buf = malloc(bufsize);
if (!buf)
{
printf("Error\n");
ret = -1;
return ret;
}
off_t offset = 0;
do {
ret = read(fd, buf + offset, bufsize - offset);
if (ret < 0)
{
ret = errno;
break;
}
*len += ret;
} while (ret != 0);
/* Clean up, including closing stdin; return errno on error */
close(fd);
if (ret)
free(buf);
else
*buffp = buf;
return ret;
}
static int firmware_fdt_read_chunk(const char *filename, char *buf, int len, int offset)
{
int fd = 0; /* assume stdin */
int ret = 0;
fd = open(filename, O_RDONLY);
if (fd < 0)
return errno;
struct stat st;
stat(filename, &st);
size_t file_size = st.st_size;
if (file_size < offset || offset < 0)
{
printf("Bad offset: %d/%ld\n", offset, file_size);
return -1;
}
if (file_size < offset + len)
len = file_size - offset;
if (offset)
lseek(fd, offset, SEEK_SET);
/* Loop until we have read everything */
size_t readed = 0;
do {
ret = read(fd, buf + readed, len - readed);
if (ret < 0)
{
ret = errno;
break;
}
readed += ret;
} while (ret != 0 && readed < len);
/* Clean up, including closing stdin; return errno on error */
close(fd);
memset(buf + readed, 0, len - readed);
return readed;
}
int a()
{
int len = 0;
char *buf = NULL;
const char *filename = "/tmp/esr1x.firmware";
firmware_fdt_read_blob(filename, &buf, &len);
printf("A:\n");
int node;
node = fdt_path_offset(buf, "/images/sbi@1");
printf("fdt_path_offset of sbi@1: %x\n", node);
int d = 0;
#if 0
node = fdt_next_node(buf, 0, &d);
node = fdt_next_node(buf, node, &d);
node = fdt_next_node(buf, node, &d);
node = fdt_next_node(buf, node, &d);
node = fdt_next_node(buf, node, &d);
node = fdt_next_node(buf, node, &d);
node = fdt_next_node(buf, node, &d);
node = fdt_next_node(buf, node, &d);
printf("node: %x\n", node);
#endif
return 0;
}
static inline const void *fdt_offset_ptr_(const void *fdt, const void *buf, int buf_offset)
{
debug("fdt_offset_ptr_: %p + %d + %x\n", buf, fdt_off_dt_struct(fdt), buf_offset);
return (const char *)buf + fdt_off_dt_struct(fdt) + buf_offset;
}
#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
/* Do not touch any forward data, caller will validate it later */
int guess_next_tag(char * fdt, int offset, char * buf)
{
const unsigned * val = fdt_offset_ptr_(fdt, buf, 0);
unsigned tag = fdt32_to_cpu(*val);
char * ptr = NULL;
switch (tag) {
case FDT_BEGIN_NODE:
ptr = (char *)val;
ptr += FDT_TAGSIZE;
debug("Starts: '%s'\n", ptr);
while (*ptr != '\0')
ptr++;
offset += ptr - (char *)val + 1;
debug("skipped %d chars\n", ptr - (char *)val + 1);
break;
case FDT_PROP:
ptr = (char *)val;
ptr += FDT_TAGSIZE;
int len = fdt32_to_cpu(*((int *)ptr));
debug("It's prop: len %d\n", len);
offset += sizeof(struct fdt_property) + len;
break;
case FDT_END:
case FDT_END_NODE:
case FDT_NOP:
offset += FDT_TAGSIZE;
break;
default:
printf("Unknown label: %x\n", tag);
return -1;
}
offset = FDT_TAGALIGN(offset);
debug("Next tag offset: %x\n", offset);
return offset;
}
int is_offset_valid(char * fdt, int offset, int len)
{
unsigned int uoffset = offset;
unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
if (offset < 0)
return 0;
if ((absoffset < uoffset)
|| ((absoffset + len) < absoffset)
|| (absoffset + len) > fdt_totalsize(fdt))
return 0;
if (fdt_version(fdt) >= 0x11)
if (((uoffset + len) < uoffset)
|| ((offset + len) > fdt_size_dt_struct(fdt)))
return 0;
return 1;
}
#define WINDOW_SIZE 512
static int fdt_dump_node_data(const char *outname, const char *filename, int offset)
{
char *buf = malloc(WINDOW_SIZE);
int readed = firmware_fdt_read_chunk(filename, buf, WINDOW_SIZE, 0);
if (readed <= 0)
return -1;
char *fdt = buf;
buf = malloc(WINDOW_SIZE);
readed = firmware_fdt_read_chunk(filename, buf, WINDOW_SIZE, offset);
if (readed <= 0)
return -1;
char * strings = malloc(WINDOW_SIZE);
readed = firmware_fdt_read_chunk(filename, strings, WINDOW_SIZE, fdt_off_dt_strings(fdt));
if (readed <= 0)
return -1;
int data_offset = 0;
int data_len = 0;
do {
int nextoffset = guess_next_tag(fdt, offset, buf);
readed = firmware_fdt_read_chunk(filename, buf, WINDOW_SIZE, nextoffset);
debug("DUMP: Read additional %d bytes from offset %x\n", readed, nextoffset);
int valid = is_offset_valid(fdt, offset, nextoffset - offset);
if (!valid)
{
printf("DUMP: next tag is not valid! (%d %d)\n", offset, nextoffset);
break;
}
debug("DUMP: Offset %x is valid\n", nextoffset);
offset = nextoffset;
if (FDT_PROP == fdt32_to_cpu(*(int *)(fdt_offset_ptr_(fdt, buf, 0))))
{
const struct fdt_property *prop = fdt_offset_ptr_(fdt, buf, 0);
debug("prop->nameoff: %x (%x)\n", fdt32_to_cpu(prop->nameoff), fdt_off_dt_strings(fdt));
char * name = strings + fdt32_to_cpu(prop->nameoff);
debug("Property name: '%s'\n", name);
if (strcmp(name, "data"))
continue;
data_offset = prop->data - (char *)prop + offset;
data_len = fdt32_to_cpu(prop->len);
debug("Property \"data\" at offset %x len %d\n", data_offset, data_len);
break;
}
} while (FDT_END != fdt32_to_cpu(*(int *)(fdt_offset_ptr_(fdt, buf, 0))));
char * data = malloc(data_len);
readed = firmware_fdt_read_chunk(filename, data, data_len,
fdt_off_dt_struct(fdt) + data_offset);
if (readed <= 0)
return -1;
#if 1
int fd = 0; /* assume stdin */
int ret = 0;
char out_filename[128] = "";
snprintf(out_filename, 128, "/tmp/%s", outname);
*strchr(out_filename, '@') = '_';
debug("Open file '%s'\n", out_filename);
fd = open(out_filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
{
printf("Error: %d\n", errno);
return -errno;
}
size_t writed = 0;
do {
ret = write(fd, data + writed, data_len - writed);
if (ret < 0)
{
ret = -errno;
break;
}
writed += ret;
} while (ret != 0 && writed < data_len);
/* Clean up, including closing stdin; return errno on error */
close(fd);
if (ret < 0)
return ret;
return writed;
#else
return 0;
#endif
}
int b()
{
int len = 0;
const char *filename = "/tmp/esr1x.firmware";
char *buf = malloc(WINDOW_SIZE);
int file_offset = 0;
int readed = firmware_fdt_read_chunk(filename, buf, WINDOW_SIZE, file_offset);
if (readed <= 0)
return -1;
char *fdt = buf;
buf = malloc(WINDOW_SIZE);
memcpy(buf, fdt, WINDOW_SIZE);
printf("B:\n");
int offset = 0;
struct { const char * name; int offset; } node_offsets[] = {
{ "sbi@1", 0 },
{ "uboot-spi@1", 0 },
{ "uboot-nand@1", 0 }
};
do {
if (FDT_BEGIN_NODE == fdt32_to_cpu(*(int *)(fdt_offset_ptr_(fdt, buf, 0))))
{
debug("Search starts\n");
for (int i = 0; i < sizeof(node_offsets) / sizeof(*node_offsets); i++)
{
if (!strcmp(node_offsets[i].name,
(char *)(fdt_offset_ptr_(fdt, buf, FDT_TAGSIZE))))
{
node_offsets[i].offset = offset;
}
}
debug("Search ends\n");
}
int nextoffset = guess_next_tag(fdt, offset, buf);
readed = firmware_fdt_read_chunk(filename, buf, WINDOW_SIZE, nextoffset);
debug("Read additional %d bytes from offset %x\n", readed, nextoffset);
int valid = is_offset_valid(fdt, offset, nextoffset - offset);
if (!valid)
{
printf("next tag is not valid! (%d %d)\n", offset, nextoffset);
break;
}
debug("Offset %x is valid\n", nextoffset);
offset = nextoffset;
} while (FDT_END != fdt32_to_cpu(*(int *)(fdt_offset_ptr_(fdt, buf, 0))));
debug("Yay\n");
for (int i = 0; i < sizeof(node_offsets) / sizeof(*node_offsets); i++)
{
printf("%s offset: %x\n", node_offsets[i].name, node_offsets[i].offset);
readed = firmware_fdt_read_chunk(filename, buf, WINDOW_SIZE,
node_offsets[i].offset);
int writed = fdt_dump_node_data(node_offsets[i].name, filename, node_offsets[i].offset);
printf("Written %d bytes\n", writed);
}
return 0;
}
int main(int argc, char *argv[])
{
a();
b();
return 0;
}