#include #include #include #include #include #include #include #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_node_get_prop(char * filename, char * fdt, char * strings, int offset, const char * prop_name, int * data_offset, int * data_len) { char * buf = malloc(WINDOW_SIZE); int readed = firmware_fdt_read_chunk(filename, buf, WINDOW_SIZE, offset); if (readed <= 0) return -1; *data_offset = 0; *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); return 0; } } while (FDT_END != fdt32_to_cpu(*(int *)(fdt_offset_ptr_(fdt, buf, 0)))); return -1; } static int fdt_dump_node_data(char * fdt, char * strings, const char *outname, const char *filename, int offset) { int data_offset = 0; int data_len = 0; if (fdt_node_get_prop(filename, fdt, strings, offset, "data", &data_offset, &data_len)) return -1; char * data = malloc(data_len); int 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"); char * strings = malloc(WINDOW_SIZE); readed = firmware_fdt_read_chunk(filename, strings, WINDOW_SIZE, fdt_off_dt_strings(fdt)); if (readed <= 0) return -1; 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(fdt, strings, 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; }