From ea679de8371e4e74fe4e78fb8df8e5df19efffdc Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Fri, 29 Sep 2017 18:22:30 +0200 Subject: atjboottool: put afi and fw code in its own files Change-Id: I16347ebee0f82d5fdf32f5aa8f955c07fe148eba --- utils/atj2137/atjboottool/Makefile | 2 +- utils/atj2137/atjboottool/afi.c | 208 ++++++++++++++++ utils/atj2137/atjboottool/afi.h | 8 +- utils/atj2137/atjboottool/atjboottool.c | 416 +++----------------------------- utils/atj2137/atjboottool/fw.c | 213 ++++++++++++++++ utils/atj2137/atjboottool/fw.h | 34 +++ utils/atj2137/atjboottool/fwu.c | 10 +- 7 files changed, 499 insertions(+), 392 deletions(-) create mode 100644 utils/atj2137/atjboottool/afi.c create mode 100644 utils/atj2137/atjboottool/fw.c create mode 100644 utils/atj2137/atjboottool/fw.h (limited to 'utils') diff --git a/utils/atj2137/atjboottool/Makefile b/utils/atj2137/atjboottool/Makefile index 31c315d4fd..8d96bcdde0 100644 --- a/utils/atj2137/atjboottool/Makefile +++ b/utils/atj2137/atjboottool/Makefile @@ -10,7 +10,7 @@ all: $(BINS) %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -atjboottool: atjboottool.o fwu.o misc.o atj_tables.o +atjboottool: atjboottool.o fwu.o fw.o afi.o misc.o atj_tables.o $(LD) -o $@ $^ $(LDFLAGS) clean: diff --git a/utils/atj2137/atjboottool/afi.c b/utils/atj2137/atjboottool/afi.c new file mode 100644 index 0000000000..1ce9d0c01e --- /dev/null +++ b/utils/atj2137/atjboottool/afi.c @@ -0,0 +1,208 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2017 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include "misc.h" +#include "afi.h" + +/** part of this work comes from s1mp3/s1fwx */ + +#define AFI_ENTRIES 126 +#define AFI_SIG_SIZE 4 + +struct afi_hdr_t +{ + uint8_t sig[AFI_SIG_SIZE]; + uint16_t vendor_id; + uint16_t product_id; + uint8_t ver_id[2]; + uint8_t ext_ver_id[2]; + uint8_t year[2]; + uint8_t month; + uint8_t day; + uint32_t afi_size; + uint32_t res[3]; +} __attribute__((packed)); + +struct afi_entry_t +{ + char name[8]; + char ext[3]; + char type; + uint32_t addr; + uint32_t offset; + uint32_t size; + char desc[4]; + uint32_t checksum; +} __attribute__((packed)); + +struct afi_post_hdr_t +{ + uint8_t res[28]; + uint32_t checksum; +} __attribute__((packed)); + +struct afi_t +{ + struct afi_hdr_t hdr; + struct afi_entry_t entry[AFI_ENTRIES]; + struct afi_post_hdr_t post; +}; + +#define AFI_ENTRY_BREC 'B' +#define AFI_ENTRY_FWSC 'F' +#define AFI_ENTRY_ADFUS 'A' +#define AFI_ENTRY_FW 'I' + +#define AFI_ENTRY_DLADR_BREC 0x00000006 // 'B' +#define AFI_ENTRY_DLADR_FWSC 0x00020008 // 'F' +#define AFI_ENTRY_DLADR_ADFUS 0x000C0008 // 'A' +#define AFI_ENTRY_DLADR_ADFU 0x00000000 // 'U' +#define AFI_ENTRY_DLADR_FW 0x00000011 // 'I' + +const uint8_t g_afi_signature[AFI_SIG_SIZE] = +{ + 'A', 'F', 'I', 0 +}; + +uint32_t afi_checksum(void *ptr, size_t size) +{ + uint32_t crc = 0; + uint32_t *cp = ptr; + for(; size >= 4; size -= 4) + crc += *cp++; + if(size == 1) + crc += *(uint8_t *)cp; + else if(size == 2) + crc += *(uint16_t *)cp; + else if(size == 3) + crc += *(uint16_t *)cp + ((*(uint8_t *)(cp + 2)) << 16); + return crc; +} + +static void build_filename(char buf[16], struct afi_entry_t *ent) +{ + int pos = 0; + for(int i = 0; i < 8 && ent->name[i] != ' '; i++) + buf[pos++] = ent->name[i]; + buf[pos++] = '.'; + for(int i = 0; i < 3 && ent->ext[i] != ' '; i++) + buf[pos++] = ent->ext[i]; + buf[pos] = 0; +} + +int afi_unpack(uint8_t *buf, size_t size, afi_extract_callback_t unpack_cb) +{ + struct afi_t *afi = (void *)buf; + + if(size < sizeof(struct afi_t)) + { + cprintf(GREY, "File too small\n"); + return 1; + } + cprintf(BLUE, "Header\n"); + cprintf(GREEN, " Signature:"); + for(int i = 0; i < AFI_SIG_SIZE; i++) + cprintf(YELLOW, " %02x", afi->hdr.sig[i]); + if(memcmp(afi->hdr.sig, g_afi_signature, AFI_SIG_SIZE) == 0) + cprintf(RED, " Ok\n"); + else + { + cprintf(RED, " Mismatch\n"); + return 1; + } + + cprintf_field(" Vendor ID: ", "0x%x\n", afi->hdr.vendor_id); + cprintf_field(" Product ID: ", "0x%x\n", afi->hdr.product_id); + cprintf_field(" Version: ", "%x.%x\n", afi->hdr.ver_id[0], afi->hdr.ver_id[1]); + cprintf_field(" Ext Version: ", "%x.%x\n", afi->hdr.ext_ver_id[0], + afi->hdr.ext_ver_id[1]); + cprintf_field(" Date: ", "%02x/%02x/%02x%02x\n", afi->hdr.day, afi->hdr.month, + afi->hdr.year[0], afi->hdr.year[1]); + + cprintf_field(" AFI size: ", "%d ", afi->hdr.afi_size); + if(afi->hdr.afi_size == size) + cprintf(RED, " Ok\n"); + else if(afi->hdr.afi_size < size) + cprintf(RED, " Ok (file greater than archive)\n"); + else + { + cprintf(RED, " Error (file too small)\n"); + return 1; + } + + cprintf_field(" Reserved: ", "%x %x %x\n", afi->hdr.res[0], + afi->hdr.res[1], afi->hdr.res[2]); + + cprintf(BLUE, "Entries\n"); + for(int i = 0; i < AFI_ENTRIES; i++) + { + if(afi->entry[i].name[0] == 0) + continue; + struct afi_entry_t *entry = &afi->entry[i]; + char filename[16]; + build_filename(filename, entry); + cprintf(RED, " %s\n", filename); + cprintf_field(" Type: ", "%02x", entry->type); + if(isprint(entry->type)) + cprintf(RED, " %c", entry->type); + printf("\n"); + cprintf_field(" Addr: ", "0x%x\n", entry->addr); + cprintf_field(" Offset: ", "0x%x\n", entry->offset); + cprintf_field(" Size: ", "0x%x\n", entry->size); + cprintf_field(" Desc: ", "%.4s\n", entry->desc); + cprintf_field(" Checksum: ", "0x%x ", entry->checksum); + uint32_t chk = afi_checksum(buf + entry->offset, entry->size); + if(chk != entry->checksum) + { + cprintf(RED, "Mismatch\n"); + return 1; + } + else + cprintf(RED, "Ok\n"); + int ret = unpack_cb(filename, buf + entry->offset, entry->size); + if(ret != 0) + return ret; + } + + cprintf(BLUE, "Post Header\n"); + cprintf_field(" Checksum: ", "%x ", afi->post.checksum); + uint32_t chk = afi_checksum(buf, sizeof(struct afi_t) - 4); + if(chk != afi->post.checksum) + { + cprintf(RED, "Mismatch\n"); + return 1; + } + else + cprintf(RED, "Ok\n"); + + return 0; +} + +bool afi_check(uint8_t *buf, size_t size) +{ + struct afi_hdr_t *hdr = (void *)buf; + + if(size < sizeof(struct afi_hdr_t)) + return false; + return memcmp(hdr->sig, g_afi_signature, AFI_SIG_SIZE) == 0; +} diff --git a/utils/atj2137/atjboottool/afi.h b/utils/atj2137/atjboottool/afi.h index bb264cefdf..6756364d2a 100644 --- a/utils/atj2137/atjboottool/afi.h +++ b/utils/atj2137/atjboottool/afi.h @@ -23,7 +23,13 @@ #include -/* Check if a file looks like a FWU file */ +uint32_t afi_checksum(void *ptr, size_t size); +/* Unpack an AFI file: the callback function will be called once for each file in the archive with + * its name and content. If the callback returns a nonzero value, the function will stop and return + * that value. Returns 0 on success */ +typedef int (*afi_extract_callback_t)(const char *name, uint8_t *buf, size_t size); +int afi_unpack(uint8_t *buf, size_t size, afi_extract_callback_t cb); +/* Check if a file looks like an AFI file */ bool afi_check(uint8_t *buf, size_t size); #endif /* __AFI_H__ */ diff --git a/utils/atj2137/atjboottool/atjboottool.c b/utils/atj2137/atjboottool/atjboottool.c index 676fdae199..d0ad1b468b 100644 --- a/utils/atj2137/atjboottool/atjboottool.c +++ b/utils/atj2137/atjboottool/atjboottool.c @@ -29,6 +29,8 @@ #include #include "misc.h" #include "fwu.h" +#include "afi.h" +#include "fw.h" bool g_debug = false; char *g_out_prefix = NULL; @@ -89,407 +91,51 @@ static int do_fwu(uint8_t *buf, size_t size, enum fwu_mode_t mode) } } -/** - * AFI - * - * part of this work comes from s1mp3/s1fwx - **/ - -#define AFI_ENTRIES 126 -#define AFI_SIG_SIZE 4 - -struct afi_hdr_t -{ - uint8_t sig[AFI_SIG_SIZE]; - uint16_t vendor_id; - uint16_t product_id; - uint8_t ver_id[2]; - uint8_t ext_ver_id[2]; - uint8_t year[2]; - uint8_t month; - uint8_t day; - uint32_t afi_size; - uint32_t res[3]; -} __attribute__((packed)); - -struct afi_entry_t -{ - char name[8]; - char ext[3]; - char type; - uint32_t addr; - uint32_t offset; - uint32_t size; - char desc[4]; - uint32_t checksum; -} __attribute__((packed)); - -struct afi_post_hdr_t -{ - uint8_t res[28]; - uint32_t checksum; -} __attribute__((packed)); - -struct afi_t -{ - struct afi_hdr_t hdr; - struct afi_entry_t entry[AFI_ENTRIES]; - struct afi_post_hdr_t post; -}; - -#define AFI_ENTRY_BREC 'B' -#define AFI_ENTRY_FWSC 'F' -#define AFI_ENTRY_ADFUS 'A' -#define AFI_ENTRY_FW 'I' - -#define AFI_ENTRY_DLADR_BREC 0x00000006 // 'B' -#define AFI_ENTRY_DLADR_FWSC 0x00020008 // 'F' -#define AFI_ENTRY_DLADR_ADFUS 0x000C0008 // 'A' -#define AFI_ENTRY_DLADR_ADFU 0x00000000 // 'U' -#define AFI_ENTRY_DLADR_FW 0x00000011 // 'I' - -const uint8_t g_afi_signature[AFI_SIG_SIZE] = +static int unpack_afi_fw_cb(const char *filename, uint8_t *buf, size_t size) { - 'A', 'F', 'I', 0 -}; + char *name = malloc(strlen(g_out_prefix) + strlen(filename) + 16); + sprintf(name, "%s%s", g_out_prefix, filename); -static uint32_t afi_checksum(void *ptr, int size) -{ - uint32_t crc = 0; - uint32_t *cp = ptr; - for(; size >= 4; size -= 4) - crc += *cp++; - if(size == 1) - crc += *(uint8_t *)cp; - else if(size == 2) - crc += *(uint16_t *)cp; - else if(size == 3) - crc += *(uint16_t *)cp + ((*(uint8_t *)(cp + 2)) << 16); - return crc; -} - -static void build_filename(char buf[16], struct afi_entry_t *ent) -{ - int pos = 0; - for(int i = 0; i < 8 && ent->name[i] != ' '; i++) - buf[pos++] = ent->name[i]; - buf[pos++] = '.'; - for(int i = 0; i < 3 && ent->ext[i] != ' '; i++) - buf[pos++] = ent->ext[i]; - buf[pos] = 0; -} - -static int do_afi(uint8_t *buf, int size) -{ - struct afi_t *afi = (void *)buf; - - if(size < (int)sizeof(struct afi_t)) - { - cprintf(GREY, "File too small\n"); - return 1; - } - cprintf(BLUE, "Header\n"); - cprintf(GREEN, " Signature:"); - for(int i = 0; i < AFI_SIG_SIZE; i++) - cprintf(YELLOW, " %02x", afi->hdr.sig[i]); - if(memcmp(afi->hdr.sig, g_afi_signature, AFI_SIG_SIZE) == 0) - cprintf(RED, " Ok\n"); - else + cprintf(GREY, "Unpacking to %s... ", name); + FILE *f = fopen(name, "wb"); + if(f) { - cprintf(RED, " Mismatch\n"); - return 1; + fwrite(buf, size, 1, f); + fclose(f); + cprintf(RED, "Ok\n"); + return 0; } - - cprintf_field(" Vendor ID: ", "0x%x\n", afi->hdr.vendor_id); - cprintf_field(" Product ID: ", "0x%x\n", afi->hdr.product_id); - cprintf_field(" Version: ", "%x.%x\n", afi->hdr.ver_id[0], afi->hdr.ver_id[1]); - cprintf_field(" Ext Version: ", "%x.%x\n", afi->hdr.ext_ver_id[0], - afi->hdr.ext_ver_id[1]); - cprintf_field(" Date: ", "%02x/%02x/%02x%02x\n", afi->hdr.day, afi->hdr.month, - afi->hdr.year[0], afi->hdr.year[1]); - - cprintf_field(" AFI size: ", "%d ", afi->hdr.afi_size); - if((int)afi->hdr.afi_size == size) - cprintf(RED, " Ok\n"); - else if((int)afi->hdr.afi_size < size) - cprintf(RED, " Ok (file greater than archive)\n"); else { - cprintf(RED, " Error (file too small)\n"); + color(RED); + perror("Failed"); return 1; } - - cprintf_field(" Reserved: ", "%x %x %x\n", afi->hdr.res[0], - afi->hdr.res[1], afi->hdr.res[2]); - - build_out_prefix(".fw", "", true); - - cprintf(BLUE, "Entries\n"); - for(int i = 0; i < AFI_ENTRIES; i++) - { - if(afi->entry[i].name[0] == 0) - continue; - struct afi_entry_t *entry = &afi->entry[i]; - char filename[16]; - build_filename(filename, entry); - cprintf(RED, " %s\n", filename); - cprintf_field(" Type: ", "%02x", entry->type); - if(isprint(entry->type)) - cprintf(RED, " %c", entry->type); - printf("\n"); - cprintf_field(" Addr: ", "0x%x\n", entry->addr); - cprintf_field(" Offset: ", "0x%x\n", entry->offset); - cprintf_field(" Size: ", "0x%x\n", entry->size); - cprintf_field(" Desc: ", "%.4s\n", entry->desc); - cprintf_field(" Checksum: ", "0x%x ", entry->checksum); - uint32_t chk = afi_checksum(buf + entry->offset, entry->size); - cprintf(RED, "%s\n", chk == entry->checksum ? "Ok" : "Mismatch"); - - char *name = malloc(strlen(g_out_prefix) + strlen(filename) + 16); - sprintf(name, "%s%s", g_out_prefix, filename); - - cprintf(GREY, "Unpacking to %s... ", name); - FILE *f = fopen(name, "wb"); - if(f) - { - fwrite(buf + entry->offset, entry->size, 1, f); - fclose(f); - cprintf(RED, "Ok\n"); - } - else - cprintf(RED, "Failed: %m\n"); - } - - cprintf(BLUE, "Post Header\n"); - cprintf_field(" Checksum: ", "%x ", afi->post.checksum); - uint32_t chk = afi_checksum(buf, sizeof(struct afi_t) - 4); - cprintf(RED, "%s\n", chk == afi->post.checksum ? "Ok" : "Mismatch"); - - return 0; } -bool afi_check(uint8_t *buf, int size) +static int do_afi(uint8_t *buf, size_t size) { - struct afi_hdr_t *hdr = (void *)buf; - - if(size < (int)sizeof(struct afi_hdr_t)) - return false; - return memcmp(hdr->sig, g_afi_signature, AFI_SIG_SIZE) == 0; -} - -/** - * FW - **/ - -#define FW_SIG_SIZE 4 - -#define FW_ENTRIES 240 - -struct fw_entry_t -{ - char name[8]; - char ext[3]; - uint8_t attr; - uint8_t res[2]; - uint16_t version; - uint32_t block_offset; // offset shift by 9 - uint32_t size; - uint32_t unk; - uint32_t checksum; -} __attribute__((packed)); - -struct fw_hdr_t -{ - uint8_t sig[FW_SIG_SIZE]; - uint32_t res[4]; - uint8_t year[2]; - uint8_t month; - uint8_t day; - uint16_t usb_vid; - uint16_t usb_pid; - uint32_t checksum; - char productor[16]; - char str2[16]; - char str3[32]; - char dev_name[32]; - uint8_t res2[8 * 16]; - char usb_name1[8]; - char usb_name2[8]; - char res3[4 * 16 + 1]; - char mtp_name1[33]; - char mtp_name2[33]; - char mtp_ver[33]; - uint16_t mtp_vid; - uint16_t mtp_pid; - char fw_ver[64]; - uint32_t res4[2]; - - struct fw_entry_t entry[FW_ENTRIES]; -} __attribute__((packed)); - -/* the s1fwx source code has a layout but it does not make any sense for firmwares - * found in ATJ2127 for example. In doubt just don't do anything */ -struct fw_hdr_f0_t -{ - uint8_t sig[FW_SIG_SIZE]; - uint8_t res[12]; - uint32_t checksum; - uint8_t res2[492]; - - struct fw_entry_t entry[FW_ENTRIES]; -} __attribute__((packed)); - -const uint8_t g_fw_signature_f2[FW_SIG_SIZE] = -{ - 0x55, 0xaa, 0xf2, 0x0f -}; - -const uint8_t g_fw_signature_f0[FW_SIG_SIZE] = -{ - 0x55, 0xaa, 0xf0, 0x0f -}; - -static void build_filename_fw(char buf[16], struct fw_entry_t *ent) -{ - int pos = 0; - for(int i = 0; i < 8 && ent->name[i] != ' '; i++) - buf[pos++] = ent->name[i]; - buf[pos++] = '.'; - for(int i = 0; i < 3 && ent->ext[i] != ' '; i++) - buf[pos++] = ent->ext[i]; - buf[pos] = 0; + build_out_prefix(".fw", "", true); + return afi_unpack(buf, size, &unpack_afi_fw_cb); } -static int do_fw(uint8_t *buf, int size) +static int do_fw(uint8_t *buf, size_t size) { - struct fw_hdr_t *hdr = (void *)buf; - - if(size < (int)sizeof(struct fw_hdr_t)) - { - cprintf(GREY, "File too small\n"); - return 1; - } - cprintf(BLUE, "Header\n"); - cprintf(GREEN, " Signature:"); - for(int i = 0; i < FW_SIG_SIZE; i++) - cprintf(YELLOW, " %02x", hdr->sig[i]); - int variant = 0; - if(memcmp(hdr->sig, g_fw_signature_f2, FW_SIG_SIZE) == 0) - { - variant = 0xf2; - cprintf(RED, " Ok (f2 variant)\n"); - } - else if(memcmp(hdr->sig, g_fw_signature_f0, FW_SIG_SIZE) == 0) - { - variant = 0xf0; - cprintf(RED, " Ok (f0 variant)\n"); - } - else - { - cprintf(RED, " Mismatch\n"); - return 1; - } - - /* both variants have the same header size, only the fields differ */ - if(variant == 0xf2) - { - cprintf_field(" USB VID: ", "0x%x\n", hdr->usb_vid); - cprintf_field(" USB PID: ", "0x%x\n", hdr->usb_pid); - cprintf_field(" Date: ", "%x/%x/%02x%02x\n", hdr->day, hdr->month, hdr->year[0], hdr->year[1]); - cprintf_field(" Checksum: ", "%x\n", hdr->checksum); - cprintf_field(" Productor: ", "%.16s\n", hdr->productor); - cprintf_field(" String 2: ", "%.16s\n", hdr->str2); - cprintf_field(" String 3: ", "%.32s\n", hdr->str3); - cprintf_field(" Device Name: ", "%.32s\n", hdr->dev_name); - cprintf(GREEN, " Unknown:\n"); - for(int i = 0; i < 8; i++) - { - cprintf(YELLOW, " "); - for(int j = 0; j < 16; j++) - cprintf(YELLOW, "%02x ", hdr->res2[i * 16 + j]); - cprintf(YELLOW, "\n"); - } - cprintf_field(" USB Name 1: ", "%.8s\n", hdr->usb_name1); - cprintf_field(" USB Name 2: ", "%.8s\n", hdr->usb_name2); - cprintf_field(" MTP Name 1: ", "%.32s\n", hdr->mtp_name1); - cprintf_field(" MTP Name 2: ", "%.32s\n", hdr->mtp_name2); - cprintf_field(" MTP Version: ", "%.32s\n", hdr->mtp_ver); - - cprintf_field(" MTP VID: ", "0x%x\n", hdr->mtp_vid); - cprintf_field(" MTP PID: ", "0x%x\n", hdr->mtp_pid); - cprintf_field(" FW Version: ", "%.64s\n", hdr->fw_ver); - } - else - { - /* struct fw_hdr_f0_t *hdr_f0 = (void *)hdr; */ - cprintf(GREEN, " Header not dumped because format is unclear.\n"); - } - build_out_prefix(".unpack", "", true); - - cprintf(BLUE, "Entries\n"); - for(int i = 0; i < AFI_ENTRIES; i++) - { - if(hdr->entry[i].name[0] == 0) - continue; - struct fw_entry_t *entry = &hdr->entry[i]; - char filename[16]; - build_filename_fw(filename, entry); - cprintf(RED, " %s\n", filename); - cprintf_field(" Attr: ", "%02x\n", entry->attr); - cprintf_field(" Offset: ", "0x%x\n", entry->block_offset << 9); - cprintf_field(" Size: ", "0x%x\n", entry->size); - cprintf_field(" Unknown: ", "%x\n", entry->unk); - cprintf_field(" Checksum: ", "0x%x ", entry->checksum); - uint32_t chk = afi_checksum(buf + (entry->block_offset << 9), entry->size); - cprintf(RED, "%s\n", chk == entry->checksum ? "Ok" : "Mismatch"); - if(g_out_prefix) - { - char *name = malloc(strlen(g_out_prefix) + strlen(filename) + 16); - sprintf(name, "%s%s", g_out_prefix, filename); - - cprintf(GREY, "Unpacking to %s... ", name); - FILE *f = fopen(name, "wb"); - if(f) - { - fwrite(buf + (entry->block_offset << 9), entry->size, 1, f); - fclose(f); - cprintf(RED, "Ok\n"); - } - else - cprintf(RED, "Failed: %m\n"); - } - } - - return 0; -} - -static bool check_fw(uint8_t *buf, int size) -{ - struct fw_hdr_t *hdr = (void *)buf; - - if(size < (int)sizeof(struct fw_hdr_t)) - return false; - if(memcmp(hdr->sig, g_fw_signature_f2, FW_SIG_SIZE) == 0) - return true; - if(memcmp(hdr->sig, g_fw_signature_f0, FW_SIG_SIZE) == 0) - return true; - return false; + return fw_unpack(buf, size, &unpack_afi_fw_cb); } - static void usage(void) { printf("Usage: atjboottool [options] firmware\n"); printf("Options:\n"); - printf(" -o \tSet output prefix\n"); - printf(" -f/--force\tForce to continue on errors\n"); - printf(" -?/--help\tDisplay this message\n"); - printf(" -d/--debug\tDisplay debug messages\n"); - printf(" -c/--no-color\tDisable color output\n"); - printf(" --fwu\tUnpack a FWU firmware file\n"); - printf(" --afi\tUnpack a AFI archive file\n"); - printf(" --fw\tUnpack a FW archive file\n"); - printf(" --atj2127\tForce ATJ2127 decryption mode\n"); + printf(" -o Set output file or output prefix\n"); + printf(" -h/--help Display this message\n"); + printf(" -d/--debug Display debug messages\n"); + printf(" -c/--no-color Disable color output\n"); + printf(" --fwu Unpack a FWU firmware file\n"); + printf(" --afi Unpack a AFI archive file\n"); + printf(" --fw Unpack a FW archive file\n"); + printf(" --atj2127 Force ATJ2127 decryption mode\n"); printf("The default is to try to guess the format.\n"); printf("If several formats are specified, all are tried.\n"); printf("If no output prefix is specified, a default one is picked.\n"); @@ -507,7 +153,7 @@ int main(int argc, char **argv) { static struct option long_options[] = { - {"help", no_argument, 0, '?'}, + {"help", no_argument, 0, 'h'}, {"debug", no_argument, 0, 'd'}, {"no-color", no_argument, 0, 'c'}, {"fwu", no_argument, 0, 'u'}, @@ -517,7 +163,7 @@ int main(int argc, char **argv) {0, 0, 0, 0} }; - int c = getopt_long(argc, argv, "?dcfo:a1", long_options, NULL); + int c = getopt_long(argc, argv, "hdco:a2", long_options, NULL); if(c == -1) break; switch(c) @@ -531,7 +177,7 @@ int main(int argc, char **argv) g_debug = true; break; break; - case '?': + case 'h': usage(); break; case 'o': @@ -591,7 +237,7 @@ int main(int argc, char **argv) ret = do_fwu(buf, size, fwu_mode); else if(try_afi || afi_check(buf, size)) ret = do_afi(buf, size); - else if(try_fw || check_fw(buf, size)) + else if(try_fw || fw_check(buf, size)) ret = do_fw(buf, size); else { diff --git a/utils/atj2137/atjboottool/fw.c b/utils/atj2137/atjboottool/fw.c new file mode 100644 index 0000000000..07ce4e2f31 --- /dev/null +++ b/utils/atj2137/atjboottool/fw.c @@ -0,0 +1,213 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2017 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include "misc.h" +#include "fw.h" +#include "afi.h" + +#define FW_SIG_SIZE 4 + +#define FW_ENTRIES 240 + +struct fw_entry_t +{ + char name[8]; + char ext[3]; + uint8_t attr; + uint8_t res[2]; + uint16_t version; + uint32_t block_offset; // offset shift by 9 + uint32_t size; + uint32_t unk; + uint32_t checksum; +} __attribute__((packed)); + +struct fw_hdr_t +{ + uint8_t sig[FW_SIG_SIZE]; + uint32_t res[4]; + uint8_t year[2]; + uint8_t month; + uint8_t day; + uint16_t usb_vid; + uint16_t usb_pid; + uint32_t checksum; + char productor[16]; + char str2[16]; + char str3[32]; + char dev_name[32]; + uint8_t res2[8 * 16]; + char usb_name1[8]; + char usb_name2[8]; + char res3[4 * 16 + 1]; + char mtp_name1[33]; + char mtp_name2[33]; + char mtp_ver[33]; + uint16_t mtp_vid; + uint16_t mtp_pid; + char fw_ver[64]; + uint32_t res4[2]; + + struct fw_entry_t entry[FW_ENTRIES]; +} __attribute__((packed)); + +/* the s1fwx source code has a layout but it does not make any sense for firmwares + * found in ATJ2127 for example. In doubt just don't do anything */ +struct fw_hdr_f0_t +{ + uint8_t sig[FW_SIG_SIZE]; + uint8_t res[12]; + uint32_t checksum; + uint8_t res2[492]; + + struct fw_entry_t entry[FW_ENTRIES]; +} __attribute__((packed)); + +const uint8_t g_fw_signature_f2[FW_SIG_SIZE] = +{ + 0x55, 0xaa, 0xf2, 0x0f +}; + +const uint8_t g_fw_signature_f0[FW_SIG_SIZE] = +{ + 0x55, 0xaa, 0xf0, 0x0f +}; + +static void build_filename_fw(char buf[16], struct fw_entry_t *ent) +{ + int pos = 0; + for(int i = 0; i < 8 && ent->name[i] != ' '; i++) + buf[pos++] = ent->name[i]; + buf[pos++] = '.'; + for(int i = 0; i < 3 && ent->ext[i] != ' '; i++) + buf[pos++] = ent->ext[i]; + buf[pos] = 0; +} + +int fw_unpack(uint8_t *buf, size_t size, fw_extract_callback_t unpack_cb) +{ + struct fw_hdr_t *hdr = (void *)buf; + + if(size < sizeof(struct fw_hdr_t)) + { + cprintf(GREY, "File too small\n"); + return 1; + } + cprintf(BLUE, "Header\n"); + cprintf(GREEN, " Signature:"); + for(int i = 0; i < FW_SIG_SIZE; i++) + cprintf(YELLOW, " %02x", hdr->sig[i]); + int variant = 0; + if(memcmp(hdr->sig, g_fw_signature_f2, FW_SIG_SIZE) == 0) + { + variant = 0xf2; + cprintf(RED, " Ok (f2 variant)\n"); + } + else if(memcmp(hdr->sig, g_fw_signature_f0, FW_SIG_SIZE) == 0) + { + variant = 0xf0; + cprintf(RED, " Ok (f0 variant)\n"); + } + else + { + cprintf(RED, " Mismatch\n"); + return 1; + } + + /* both variants have the same header size, only the fields differ */ + if(variant == 0xf2) + { + cprintf_field(" USB VID: ", "0x%x\n", hdr->usb_vid); + cprintf_field(" USB PID: ", "0x%x\n", hdr->usb_pid); + cprintf_field(" Date: ", "%x/%x/%02x%02x\n", hdr->day, hdr->month, hdr->year[0], hdr->year[1]); + cprintf_field(" Checksum: ", "%x\n", hdr->checksum); + cprintf_field(" Productor: ", "%.16s\n", hdr->productor); + cprintf_field(" String 2: ", "%.16s\n", hdr->str2); + cprintf_field(" String 3: ", "%.32s\n", hdr->str3); + cprintf_field(" Device Name: ", "%.32s\n", hdr->dev_name); + cprintf(GREEN, " Unknown:\n"); + for(int i = 0; i < 8; i++) + { + cprintf(YELLOW, " "); + for(int j = 0; j < 16; j++) + cprintf(YELLOW, "%02x ", hdr->res2[i * 16 + j]); + cprintf(YELLOW, "\n"); + } + cprintf_field(" USB Name 1: ", "%.8s\n", hdr->usb_name1); + cprintf_field(" USB Name 2: ", "%.8s\n", hdr->usb_name2); + cprintf_field(" MTP Name 1: ", "%.32s\n", hdr->mtp_name1); + cprintf_field(" MTP Name 2: ", "%.32s\n", hdr->mtp_name2); + cprintf_field(" MTP Version: ", "%.32s\n", hdr->mtp_ver); + + cprintf_field(" MTP VID: ", "0x%x\n", hdr->mtp_vid); + cprintf_field(" MTP PID: ", "0x%x\n", hdr->mtp_pid); + cprintf_field(" FW Version: ", "%.64s\n", hdr->fw_ver); + } + else + { + /* struct fw_hdr_f0_t *hdr_f0 = (void *)hdr; */ + cprintf(GREEN, " Header not dumped because format is unclear.\n"); + } + + cprintf(BLUE, "Entries\n"); + for(int i = 0; i < FW_ENTRIES; i++) + { + if(hdr->entry[i].name[0] == 0) + continue; + struct fw_entry_t *entry = &hdr->entry[i]; + char filename[16]; + build_filename_fw(filename, entry); + cprintf(RED, " %s\n", filename); + cprintf_field(" Attr: ", "%02x\n", entry->attr); + cprintf_field(" Offset: ", "0x%x\n", entry->block_offset << 9); + cprintf_field(" Size: ", "0x%x\n", entry->size); + cprintf_field(" Unknown: ", "%x\n", entry->unk); + cprintf_field(" Checksum: ", "0x%x ", entry->checksum); + uint32_t chk = afi_checksum(buf + (entry->block_offset << 9), entry->size); + if(chk != entry->checksum) + { + cprintf(RED, "Mismatch\n"); + return 1; + } + else + cprintf(RED, "Ok\n"); + int ret = unpack_cb(filename, buf + (entry->block_offset << 9), entry->size); + if(ret != 0) + return ret; + } + + return 0; +} + +bool fw_check(uint8_t *buf, size_t size) +{ + struct fw_hdr_t *hdr = (void *)buf; + + if(size < sizeof(struct fw_hdr_t)) + return false; + if(memcmp(hdr->sig, g_fw_signature_f2, FW_SIG_SIZE) == 0) + return true; + if(memcmp(hdr->sig, g_fw_signature_f0, FW_SIG_SIZE) == 0) + return true; + return false; +} diff --git a/utils/atj2137/atjboottool/fw.h b/utils/atj2137/atjboottool/fw.h new file mode 100644 index 0000000000..95f8087116 --- /dev/null +++ b/utils/atj2137/atjboottool/fw.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2017 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __FW_H__ +#define __FW_H__ + +#include + +/* Unpack an AFI file: the callback function will be called once for each file in the archive with + * its name and content. If the callback returns a nonzero value, the function will stop and return + * that value. Returns 0 on success */ +typedef int (*fw_extract_callback_t)(const char *name, uint8_t *buf, size_t size); +int fw_unpack(uint8_t *buf, size_t size, fw_extract_callback_t cb); +/* Check if a file looks like an AFI file */ +bool fw_check(uint8_t *buf, size_t size); + +#endif /* __FW_H__ */ diff --git a/utils/atj2137/atjboottool/fwu.c b/utils/atj2137/atjboottool/fwu.c index 843a1cb628..44281af4f4 100644 --- a/utils/atj2137/atjboottool/fwu.c +++ b/utils/atj2137/atjboottool/fwu.c @@ -149,10 +149,10 @@ static int decode_block_A(uint8_t block[1020]) } for(int i = 20; i < 32; i++) key[i] = key[i - 20]; - + for(int i = 0; i < 992; i++) block[i] ^= key[i % 32] ^ g_check_block_A_table[i]; - + return check_block(block - 1, block + 1000, 1001); } @@ -277,7 +277,7 @@ static int process_block_A(uint8_t block[1024]) g_crypto_info_byte = block[offset - 1]; g_decode_buffer = malloc(g_decode_A_info.size); g_decode_buffer2 = malloc(g_decode_A_info.size); - + memset(g_decode_buffer, 0, g_decode_A_info.size); memset(g_decode_buffer2, 0, g_decode_A_info.size); @@ -288,7 +288,7 @@ static int process_block_A(uint8_t block[1024]) cprintf_field(" Word: ", "%d ", *(uint16_t *)&g_subblock_A[286]); check_field(*(uint16_t *)&g_subblock_A[286], 1, "Ok\n", "Mismatch\n"); - + return 0; } @@ -625,7 +625,7 @@ static int crypto3(uint32_t *a1, ptr_bundle_t *ptrs_alt, ptr_bundle_t *ptrs) static int crypto4(uint8_t *a1, ptr_bundle_t *ptrs, uint32_t *a3) { ptr_bundle_t ptrs_others; - + ptrs_others.ptrA = malloc(g_decode_A_info.size); ptrs_others.ptrB = malloc(g_decode_A_info.size); clear_memory(ptrs_others.ptrA, g_decode_A_info.nr_dwords); -- cgit