+/* Helper functions to inspect_fw() representing different output formats */
+static inline void inspect_fw_pstr(char *label, char *str)
+{
+ printf("%-23s: %s\n", label, str);
+}
+
+static inline void inspect_fw_phex(char *label, uint32_t val)
+{
+ printf("%-23s: 0x%08x\n", label, val);
+}
+
+static inline void inspect_fw_phexpost(char *label,
+ uint32_t val, char *post)
+{
+ printf("%-23s: 0x%08x (%s)\n", label, val, post);
+}
+
+static inline void inspect_fw_phexdef(char *label,
+ uint32_t val, uint32_t defval)
+{
+ printf("%-23s: 0x%08x ", label, val);
+
+ if (val == defval)
+ printf("(== OpenWrt default)\n");
+ else
+ printf("(OpenWrt default: 0x%08x)\n", defval);
+}
+
+static inline void inspect_fw_phexexp(char *label,
+ uint32_t val, uint32_t expval)
+{
+ printf("%-23s: 0x%08x ", label, val);
+
+ if (val == expval)
+ printf("(ok)\n");
+ else
+ printf("(expected: 0x%08x)\n", expval);
+}
+
+static inline void inspect_fw_phexdec(char *label, uint32_t val)
+{
+ printf("%-23s: 0x%08x / %8u bytes\n", label, val, val);
+}
+
+static inline void inspect_fw_phexdecdef(char *label,
+ uint32_t val, uint32_t defval)
+{
+ printf("%-23s: 0x%08x / %8u bytes ", label, val, val);
+
+ if (val == defval)
+ printf("(== OpenWrt default)\n");
+ else
+ printf("(OpenWrt default: 0x%08x)\n", defval);
+}
+
+static inline void inspect_fw_pmd5sum(char *label, uint8_t *val, char *text)
+{
+ int i;
+
+ printf("%-23s:", label);
+ for (i=0; i<MD5SUM_LEN; i++)
+ printf(" %02x", val[i]);
+ printf(" %s\n", text);
+}
+
+static int inspect_fw(void)
+{
+ char *buf;
+ struct fw_header *hdr;
+ uint8_t md5sum[MD5SUM_LEN];
+ struct board_info *board;
+ int ret = EXIT_FAILURE;
+
+ buf = malloc(inspect_info.file_size);
+ if (!buf) {
+ ERR("no memory for buffer!\n");
+ goto out;
+ }
+
+ ret = read_to_buf(&inspect_info, buf);
+ if (ret)
+ goto out_free_buf;
+ hdr = (struct fw_header *)buf;
+
+ inspect_fw_pstr("File name", inspect_info.file_name);
+ inspect_fw_phexdec("File size", inspect_info.file_size);
+
+ if (BE32_TO_HOST(hdr->version) != HEADER_VERSION_V1) {
+ ERR("file does not seem to have V1 header!\n");
+ goto out_free_buf;
+ }
+
+ inspect_fw_phexdec("Version 1 Header size", sizeof(struct fw_header));
+
+ if (BE32_TO_HOST(hdr->unk1) != 0)
+ inspect_fw_phexdec("Unknown value 1", hdr->unk1);
+
+ memcpy(md5sum, hdr->md5sum1, sizeof(md5sum));
+ if (BE32_TO_HOST(hdr->boot_len) == 0)
+ memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum));
+ else
+ memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum));
+ get_md5(buf, inspect_info.file_size, hdr->md5sum1);
+
+ if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) {
+ inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(*ERROR*)");
+ inspect_fw_pmd5sum(" --> expected", hdr->md5sum1, "");
+ } else {
+ inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(ok)");
+ }
+ if (BE32_TO_HOST(hdr->unk2) != 0)
+ inspect_fw_phexdec("Unknown value 2", hdr->unk2);
+ inspect_fw_pmd5sum("Header MD5Sum2", hdr->md5sum2,
+ "(purpose yet unknown, unchecked here)");
+ if (BE32_TO_HOST(hdr->unk3) != 0)
+ inspect_fw_phexdec("Unknown value 3", hdr->unk3);
+
+ printf("\n");
+
+ inspect_fw_pstr("Vendor name", hdr->vendor_name);
+ inspect_fw_pstr("Firmware version", hdr->fw_version);
+ board = find_board_by_hwid(BE32_TO_HOST(hdr->hw_id));
+ if (board) {
+ inspect_fw_phexpost("Hardware ID",
+ BE32_TO_HOST(hdr->hw_id), board->id);
+ inspect_fw_phexexp("Hardware Revision",
+ BE32_TO_HOST(hdr->hw_rev), board->hw_rev);
+ } else {
+ inspect_fw_phexpost("Hardware ID",
+ BE32_TO_HOST(hdr->hw_id), "unknown");
+ inspect_fw_phex("Hardware Revision",
+ BE32_TO_HOST(hdr->hw_rev));
+ }
+
+ printf("\n");
+
+ inspect_fw_phexdec("Kernel data offset",
+ BE32_TO_HOST(hdr->kernel_ofs));
+ inspect_fw_phexdec("Kernel data length",
+ BE32_TO_HOST(hdr->kernel_len));
+ if (board) {
+ inspect_fw_phexdef("Kernel load address",
+ BE32_TO_HOST(hdr->kernel_la),
+ board->kernel_la);
+ inspect_fw_phexdef("Kernel entry point",
+ BE32_TO_HOST(hdr->kernel_ep),
+ board->kernel_ep);
+ inspect_fw_phexdecdef("Rootfs data offset",
+ BE32_TO_HOST(hdr->rootfs_ofs),
+ board->rootfs_ofs);
+ } else {
+ inspect_fw_phex("Kernel load address",
+ BE32_TO_HOST(hdr->kernel_la));
+ inspect_fw_phex("Kernel entry point",
+ BE32_TO_HOST(hdr->kernel_ep));
+ inspect_fw_phexdec("Rootfs data offset",
+ BE32_TO_HOST(hdr->rootfs_ofs));
+ }
+ inspect_fw_phexdec("Rootfs data length",
+ BE32_TO_HOST(hdr->rootfs_len));
+ inspect_fw_phexdec("Boot loader data offset",
+ BE32_TO_HOST(hdr->boot_ofs));
+ inspect_fw_phexdec("Boot loader data length",
+ BE32_TO_HOST(hdr->boot_len));
+ inspect_fw_phexdec("Total firmware length",
+ BE32_TO_HOST(hdr->fw_length));
+
+ if (extract) {
+ FILE *fp;
+ char *filename;
+
+ printf("\n");
+
+ filename = malloc(strlen(inspect_info.file_name) + 8);
+ sprintf(filename, "%s-kernel", inspect_info.file_name);
+ printf("Extracting kernel to \"%s\"...\n", filename);
+ fp = fopen(filename, "w");
+ if (fp) {
+ if (!fwrite(buf + BE32_TO_HOST(hdr->kernel_ofs),
+ BE32_TO_HOST(hdr->kernel_len), 1, fp)) {
+ ERR("error in fwrite(): %s", strerror(errno));
+ }
+ fclose(fp);
+ } else {
+ ERR("error in fopen(): %s", strerror(errno));
+ }
+ free(filename);
+
+ filename = malloc(strlen(inspect_info.file_name) + 8);
+ sprintf(filename, "%s-rootfs", inspect_info.file_name);
+ printf("Extracting rootfs to \"%s\"...\n", filename);
+ fp = fopen(filename, "w");
+ if (fp) {
+ if (!fwrite(buf + BE32_TO_HOST(hdr->rootfs_ofs),
+ BE32_TO_HOST(hdr->rootfs_len), 1, fp)) {
+ ERR("error in fwrite(): %s", strerror(errno));
+ }
+ fclose(fp);
+ } else {
+ ERR("error in fopen(): %s", strerror(errno));
+ }
+ free(filename);
+ }
+
+ out_free_buf:
+ free(buf);
+ out:
+ return ret;
+}
+