tplink-safeloader: add EAP610 v3 and EAP613 v1
[project/firmware-utils.git] / src / tplink-safeloader.c
index 79aefd973232941fbc1dfe8d89ba3fefe8c1d3ed..53a42f7a7d33d01754b26969d1ab20fa1fb62b5f 100644 (file)
@@ -127,6 +127,57 @@ struct __attribute__((__packed__)) soft_version {
        uint32_t compat_level;
 };
 
+/*
+ * Safeloader image type
+ *   Safeloader images contain a 0x14 byte preamble with image size (big endian
+ *   UINT32) and md5 checksum (16 bytes).
+ *
+ * SAFEFLOADER_TYPE_DEFAULT
+ *   Standard preamble with size including preamble length, and checksum.
+ *   Header of 0x1000 bytes, contents of which are not specified.
+ *   Payload starts at offset 0x1014.
+ *
+ * SAFELOADER_TYPE_VENDOR
+ *   Standard preamble with size including preamble length, and checksum.
+ *   Header contains up to 0x1000 bytes of vendor data, starting with a big endian
+ *   UINT32 size, followed by that number of bytes containing (text) data.
+ *   Padded with 0xFF. Payload starts at offset 0x1014.
+ *
+ * SAFELOADER_TYPE_CLOUD
+ *   Standard preamble with size including preamble length, and checksum.
+ *   Followed by the 'fw-type:Cloud' string and some (unknown) data.
+ *   Payload starts at offset 0x1014.
+ *
+ * SAFELOADER_TYPE_QNEW
+ *   Reversed order preamble, with (apparent) md5 checksum before the image
+ *   size. The size does not include the preamble length.
+ *   Header starts with 0x3C bytes, starting with the string '?NEW' (format not
+ *   understood). Then another 0x1000 bytes follow, with the data payload
+ *   starting at 0x1050.
+ */
+enum safeloader_image_type {
+       SAFELOADER_TYPE_DEFAULT,
+       SAFELOADER_TYPE_VENDOR,
+       SAFELOADER_TYPE_CLOUD,
+       SAFELOADER_TYPE_QNEW,
+};
+
+/* Internal representation of safeloader image data */
+struct safeloader_image_info {
+       enum safeloader_image_type type;
+       size_t payload_offset;
+       struct flash_partition_entry entries[MAX_PARTITIONS];
+};
+
+#define SAFELOADER_PREAMBLE_SIZE       0x14
+#define SAFELOADER_HEADER_SIZE         0x1000
+#define SAFELOADER_PAYLOAD_OFFSET      (SAFELOADER_PREAMBLE_SIZE + SAFELOADER_HEADER_SIZE)
+
+#define SAFELOADER_QNEW_HEADER_SIZE    0x3C
+#define SAFELOADER_QNEW_PAYLOAD_OFFSET \
+       (SAFELOADER_PREAMBLE_SIZE + SAFELOADER_QNEW_HEADER_SIZE + SAFELOADER_HEADER_SIZE)
+
+#define SAFELOADER_PAYLOAD_TABLE_SIZE  0x800
 
 static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
 
@@ -920,6 +971,55 @@ static struct device_info boards[] = {
                .last_sysupgrade_partition = "file-system",
        },
 
+       /** Firmware layout for the Archer AX23 v1 */
+       {
+               .id     = "ARCHER-AX23-V1",
+               .vendor = "",
+               .support_list =
+                       "SupportList:\n"
+                       "{product_name:Archer AX23,product_ver:1.0,special_id:45550000}\n"
+                       "{product_name:Archer AX23,product_ver:1.0,special_id:4A500000}\n"
+                       "{product_name:Archer AX23,product_ver:1.0,special_id:4B520000}\n"
+                       "{product_name:Archer AX23,product_ver:1.0,special_id:52550000}\n"
+                       "{product_name:Archer AX23,product_ver:1.0.0,special_id:43410000}\n"
+                       "{product_name:Archer AX23,product_ver:1.0.0,special_id:54570000}\n"
+                       "{product_name:Archer AX23,product_ver:1.0.0,special_id:55530000}\n"
+                       "{product_name:Archer AX23,product_ver:1.20,special_id:45550000}\n"
+                       "{product_name:Archer AX23,product_ver:1.20,special_id:4A500000}\n"
+                       "{product_name:Archer AX23,product_ver:1.20,special_id:52550000}\n"
+                       "{product_name:Archer AX23,product_ver:1.20,special_id:55530000}\n"
+                       "{product_name:Archer AX1800,product_ver:1.20,special_id:45550000}\n"
+                       "{product_name:Archer AX1800,product_ver:1.20,special_id:52550000}\n",
+               .part_trail = 0x00,
+               .soft_ver = SOFT_VER_TEXT("soft_ver:3.0.3\n"),
+
+               .partitions = {
+                       {"fs-uboot", 0x00000, 0x40000},
+                       {"firmware", 0x40000, 0xf60000},
+                       {"default-mac", 0xfa0000, 0x00200},
+                       {"pin", 0xfa0200, 0x00100},
+                       {"device-id", 0xfa0300, 0x00100},
+                       {"product-info", 0xfa0400, 0x0fc00},
+                       {"default-config", 0xfb0000, 0x08000},
+                       {"ap-def-config", 0xfb8000, 0x08000},
+                       {"user-config", 0xfc0000, 0x0a000},
+                       {"ag-config", 0xfca000, 0x04000},
+                       {"certificate", 0xfce000, 0x02000},
+                       {"ap-config", 0xfd0000, 0x06000},
+                       {"router-config", 0xfd6000, 0x06000},
+                       {"favicon", 0xfdc000, 0x02000},
+                       {"logo", 0xfde000, 0x02000},
+                       {"partition-table", 0xfe0000, 0x00800},
+                       {"soft-version", 0xfe0800, 0x00100},
+                       {"support-list", 0xfe0900, 0x00400},
+                       {"profile", 0xfe0d00, 0x03000},
+                       {"extra-para", 0xfe3d00, 0x00100},
+                       {"radio", 0xff0000, 0x10000},
+                       {NULL, 0, 0}
+               },
+               .first_sysupgrade_partition = "os-image",
+               .last_sysupgrade_partition = "file-system",
+       },
        /** Firmware layout for the C2v3 */
        {
                .id     = "ARCHER-C2-V3",
@@ -1037,6 +1137,7 @@ static struct device_info boards[] = {
                .support_list =
                        "SupportList:\r\n"
                        "{product_name:Archer C59,product_ver:1.0.0,special_id:00000000}\r\n"
+                       "{product_name:Archer C59,product_ver:1.0.0,special_id:43410000}\r\n"
                        "{product_name:Archer C59,product_ver:1.0.0,special_id:45550000}\r\n"
                        "{product_name:Archer C59,product_ver:1.0.0,special_id:52550000}\r\n"
                        "{product_name:Archer C59,product_ver:1.0.0,special_id:55530000}\r\n",
@@ -1076,6 +1177,7 @@ static struct device_info boards[] = {
                .support_list =
                        "SupportList:\r\n"
                        "{product_name:Archer C59,product_ver:2.0.0,special_id:00000000}\r\n"
+                       "{product_name:Archer C59,product_ver:2.0.0,special_id:43410000}\r\n"
                        "{product_name:Archer C59,product_ver:2.0.0,special_id:45550000}\r\n"
                        "{product_name:Archer C59,product_ver:2.0.0,special_id:55530000}\r\n",
                .part_trail = 0x00,
@@ -1230,7 +1332,9 @@ static struct device_info boards[] = {
                        "{product_name:Archer A6,product_ver:3.0.0,special_id:43410000}\n"
                        "{product_name:Archer A6,product_ver:3.0.0,special_id:55530000}\n"
                        "{product_name:Archer A6,product_ver:3.0.0,special_id:54570000}\n"
-                       "{product_name:Archer A6,product_ver:3.0.0,special_id:4A500000}\n",
+                       "{product_name:Archer A6,product_ver:3.0.0,special_id:4A500000}\n"
+                       "{product_name:Archer A6,product_ver:3.20,special_id:45550000}\n"
+                       "{product_name:Archer A6,product_ver:3.20,special_id:52550000}\n",
                .part_trail = 0x00,
                .soft_ver = SOFT_VER_TEXT("soft_ver:1.0.5\n"),
 
@@ -1267,7 +1371,8 @@ static struct device_info boards[] = {
                .vendor = "",
                .support_list =
                        "SupportList:\n"
-                       "{product_name:Archer C6U,product_ver:1.0.0,special_id:45550000}\n",
+                       "{product_name:Archer C6U,product_ver:1.0.0,special_id:45550000}\n"
+                       "{product_name:Archer C6U,product_ver:1.0.0,special_id:52550000}\n",
                .part_trail = 0x00,
                .soft_ver = SOFT_VER_TEXT("soft_ver:1.0.2\n"),
 
@@ -1302,6 +1407,7 @@ static struct device_info boards[] = {
                .support_list =
                        "SupportList:\r\n"
                        "{product_name:Archer C60,product_ver:1.0.0,special_id:00000000}\r\n"
+                       "{product_name:Archer C60,product_ver:1.0.0,special_id:43410000}\r\n"
                        "{product_name:Archer C60,product_ver:1.0.0,special_id:45550000}\r\n"
                        "{product_name:Archer C60,product_ver:1.0.0,special_id:55530000}\r\n",
                .part_trail = 0x00,
@@ -1336,6 +1442,7 @@ static struct device_info boards[] = {
                .support_list =
                        "SupportList:\r\n"
                        "{product_name:Archer C60,product_ver:2.0.0,special_id:42520000}\r\n"
+                       "{product_name:Archer C60,product_ver:2.0.0,special_id:43410000}\r\n"
                        "{product_name:Archer C60,product_ver:2.0.0,special_id:45550000}\r\n"
                        "{product_name:Archer C60,product_ver:2.0.0,special_id:55530000}\r\n",
                .part_trail = 0x00,
@@ -1372,6 +1479,7 @@ static struct device_info boards[] = {
                .support_list =
                        "SupportList:\r\n"
                        "{product_name:Archer C60,product_ver:3.0.0,special_id:42520000}\r\n"
+                       "{product_name:Archer C60,product_ver:3.0.0,special_id:43410000}\r\n"
                        "{product_name:Archer C60,product_ver:3.0.0,special_id:45550000}\r\n"
                        "{product_name:Archer C60,product_ver:3.0.0,special_id:55530000}\r\n",
                .part_trail = 0x00,
@@ -1648,6 +1756,87 @@ static struct device_info boards[] = {
                .last_sysupgrade_partition = "file-system",
        },
 
+       /** Firmware layout for the Deco M5 */
+       {
+               .id = "DECO-M5",
+               .vendor = "",
+               .support_list =
+                       "SupportList:\n"
+                       "{product_name:M5,product_ver:1.0.0,special_id:55530000}\n"
+                       "{product_name:M5,product_ver:1.0.0,special_id:45550000}\n"
+                       "{product_name:M5,product_ver:1.0.0,special_id:43410000}\n"
+                       "{product_name:M5,product_ver:1.0.0,special_id:4A500000}\n"
+                       "{product_name:M5,product_ver:1.0.0,special_id:41550000}\n"
+                       "{product_name:M5,product_ver:1.0.0,special_id:4B520000}\n"
+                       "{product_name:M5,product_ver:1.0.0,special_id:49440000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:55530000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:45550000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:43410000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:4A500000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:41550000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:4B520000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:49440000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:53570000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:42340000}\n"
+                       "{product_name:M5,product_ver:3.0.0,special_id:54570000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:55530000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:45550000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:43410000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:4A500000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:41550000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:4B520000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:49440000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:53570000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:42340000}\n"
+                       "{product_name:M5,product_ver:3.2.0,special_id:54570000}\n",
+               .part_trail = 0x00,
+               .soft_ver = SOFT_VER_DEFAULT,
+
+               .partitions = {
+                       {"SBL1", 0x00000, 0x30000},
+                       {"boot-config_0", 0x30000, 0x10000},
+                       {"MIBIB", 0x40000, 0x10000},
+                       {"boot-config_1", 0x50000, 0x10000},
+                       {"QSEE", 0x60000, 0x60000},
+                       {"CDT", 0xc0000, 0x10000},
+                       {"DDRPARAMS", 0xd0000, 0x10000},
+                       {"uboot-env", 0xe0000, 0x10000},
+                       {"fs-uboot@0", 0xf0000, 0x80000},
+                       {"radio", 0x170000, 0x0fff0},
+                       {"bluetooth-XTAL", 0x17fff0, 0x00010},
+                       {"default-mac", 0x180000, 0x01000},
+                       {"device-id", 0x182000, 0x01000},
+                       {"product-info", 0x183000, 0x05000},
+                       {"support-list", 0x190000, 0x10000},
+                       {"user-config", 0x200000, 0x10000},
+                       {"device-config", 0x210000, 0x10000},
+                       {"group-info", 0x220000, 0x10000},
+                       {"partition-table@0", 0x230000, 0x02000},
+                       {"os-image@0", 0x240000, 0x300000},
+                       {"file-system@0", 0x540000, 0x790000},
+                       {"soft-version@0", 0xcd0000, 0x10000},
+                       {"profile@0", 0xce0000, 0x10000},
+                       {"default-config@0", 0xcf0000, 0x10000},
+                       {"partition-table@1", 0xd00000, 0x02000},
+                       {"fs-uboot@1", 0xd10000, 0x80000},
+                       {"os-image@1", 0xd90000, 0x400000},
+                       {"file-system@1", 0x1190000, 0xc40000},
+                       {"soft-version@1", 0x1dd0000, 0x10000},
+                       {"profile@1", 0x1de0000, 0x10000},
+                       {"default-config@1", 0x1df0000, 0x10000},
+                       {"tm-sig", 0x1e00000, 0x200000},
+                       {NULL, 0, 0}
+               },
+
+               .partition_names.partition_table = "partition-table@1",
+               .partition_names.soft_ver = "soft-version@1",
+               .partition_names.os_image = "os-image@1",
+               .partition_names.file_system = "file-system@1",
+
+               .first_sysupgrade_partition = "os-image@1",
+               .last_sysupgrade_partition = "file-system@1"
+       },
+
        /** Firmware layout for the Deco S4 v2 */
        {
                .id     = "DECO-S4-V2",
@@ -1932,6 +2121,39 @@ static struct device_info boards[] = {
                .last_sysupgrade_partition = "file-system"
        },
 
+       /** Firmware layout for the EAP610 v3/EAP613 v1 */
+       {
+               .id = "EAP610-V3",
+               .soft_ver = SOFT_VER_DEFAULT,
+               .soft_ver_compat_level = 1,
+               .support_list =
+                       "SupportList:\r\n"
+                       "EAP610(TP-Link|UN|AX1800-D):3.0\r\n"
+                       "EAP610(TP-Link|JP|AX1800-D):3.0\r\n"
+                       "EAP610(TP-Link|EG|AX1800-D):3.0\r\n"
+                       "EAP610(TP-Link|CA|AX1800-D):3.0\r\n"
+                       "EAP613(TP-Link|UN|AX1800-D):1.0 JP\r\n",
+               .part_trail = PART_TRAIL_NONE,
+
+               .partitions = {
+                       {"fs-uboot", 0x00000, 0x80000},
+                       {"partition-table", 0x80000, 0x02000},
+                       {"default-mac", 0x90000, 0x01000},
+                       {"support-list", 0x91000, 0x00100},
+                       {"product-info", 0x91100, 0x00400},
+                       {"soft-version", 0x92000, 0x00100},
+                       {"firmware", 0xa0000, 0xcf0000},
+                       {"user-config", 0xd90000, 0x60000},
+                       {"mutil-log", 0xf30000, 0x80000},
+                       {"oops", 0xfb0000, 0x40000},
+                       {"radio", 0xff0000, 0x10000},
+                       {NULL, 0, 0}
+               },
+
+               .first_sysupgrade_partition = "os-image",
+               .last_sysupgrade_partition = "file-system"
+       },
+
        /** Firmware layout for the EAP615-Wall v1 */
        {
                .id = "EAP615-WALL-V1",
@@ -2202,7 +2424,9 @@ static struct device_info boards[] = {
                        "SupportList:\n"
                        "{product_name:TL-WPA8631P,product_ver:3.0.0,special_id:41550000}\n"
                        "{product_name:TL-WPA8631P,product_ver:3.0.0,special_id:45550000}\n"
-                       "{product_name:TL-WPA8631P,product_ver:3.0.0,special_id:55530000}\n",
+                       "{product_name:TL-WPA8631P,product_ver:3.0.0,special_id:55530000}\n"
+                       "{product_name:TL-WPA8631P,product_ver:4.0.0,special_id:45550000}\n"
+                       "{product_name:TL-WPA8635P,product_ver:3.0.0,special_id:46520000}\n",
                .part_trail = 0x00,
                .soft_ver = SOFT_VER_DEFAULT,
 
@@ -2216,8 +2440,8 @@ static struct device_info boards[] = {
                        {"default-region", 0x732300, 0x00010},
                        {"product-info", 0x732400, 0x00200},
                        {"extra-para", 0x732600, 0x00200},
-                       {"soft-version", 0x732800, 0x00200},
-                       {"support-list", 0x732a00, 0x00100},
+                       {"soft-version", 0x732800, 0x00100},
+                       {"support-list", 0x732900, 0x00200},
                        {"profile", 0x732b00, 0x00100},
                        {"default-config", 0x732c00, 0x00800},
                        {"plc-type", 0x733400, 0x00020},
@@ -3184,7 +3408,7 @@ static void set_source_date_epoch() {
 /** Generates the partition-table partition */
 static struct image_partition_entry make_partition_table(const struct device_info *p)
 {
-       struct image_partition_entry entry = alloc_image_partition(p->partition_names.partition_table, 0x800);
+       struct image_partition_entry entry = alloc_image_partition(p->partition_names.partition_table, SAFELOADER_PAYLOAD_TABLE_SIZE);
 
        char *s = (char *)entry.data, *end = (char *)(s+entry.size);
 
@@ -3347,9 +3571,9 @@ static struct image_partition_entry read_file(const char *part_name, const char
 */
 static void put_partitions(uint8_t *buffer, const struct flash_partition_entry *flash_parts, const struct image_partition_entry *parts) {
        size_t i, j;
-       char *image_pt = (char *)buffer, *end = image_pt + 0x800;
+       char *image_pt = (char *)buffer, *end = image_pt + SAFELOADER_PAYLOAD_TABLE_SIZE;
 
-       size_t base = 0x800;
+       size_t base = SAFELOADER_PAYLOAD_TABLE_SIZE;
        for (i = 0; parts[i].name; i++) {
                for (j = 0; flash_parts[j].name; j++) {
                        if (!strcmp(flash_parts[j].name, parts[i].name)) {
@@ -3402,7 +3626,7 @@ static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
      1814-xxxx    Firmware partitions
 */
 static void * generate_factory_image(struct device_info *info, const struct image_partition_entry *parts, size_t *len) {
-       *len = 0x1814;
+       *len = SAFELOADER_PAYLOAD_OFFSET + SAFELOADER_PAYLOAD_TABLE_SIZE;
 
        size_t i;
        for (i = 0; parts[i].name; i++)
@@ -3417,12 +3641,12 @@ static void * generate_factory_image(struct device_info *info, const struct imag
 
        if (info->vendor) {
                size_t vendor_len = strlen(info->vendor);
-               put32(image+0x14, vendor_len);
-               memcpy(image+0x18, info->vendor, vendor_len);
+               put32(image + SAFELOADER_PREAMBLE_SIZE, vendor_len);
+               memcpy(image + SAFELOADER_PREAMBLE_SIZE + 0x4, info->vendor, vendor_len);
        }
 
-       put_partitions(image + 0x1014, info->partitions, parts);
-       put_md5(image+0x04, image+0x14, *len-0x14);
+       put_partitions(image + SAFELOADER_PAYLOAD_OFFSET, info->partitions, parts);
+       put_md5(image + 0x04, image + SAFELOADER_PREAMBLE_SIZE, *len - SAFELOADER_PREAMBLE_SIZE);
 
        return image;
 }
@@ -3559,6 +3783,7 @@ static void build_image(const char *output,
        if (strcasecmp(info->id, "ARCHER-A6-V3") == 0 ||
            strcasecmp(info->id, "ARCHER-A7-V5") == 0 ||
            strcasecmp(info->id, "ARCHER-A9-V6") == 0 ||
+           strcasecmp(info->id, "ARCHER-AX23-V1") == 0 ||
            strcasecmp(info->id, "ARCHER-C2-V3") == 0 ||
            strcasecmp(info->id, "ARCHER-C7-V4") == 0 ||
            strcasecmp(info->id, "ARCHER-C7-V5") == 0 ||
@@ -3689,7 +3914,7 @@ static int read_partition_table(
                struct flash_partition_entry *entries, size_t max_entries,
                int type)
 {
-       char buf[2048];
+       char buf[SAFELOADER_PAYLOAD_TABLE_SIZE];
        char *ptr, *end;
        const char *parthdr = NULL;
        const char *fwuphdr = "fwup-ptn";
@@ -3711,10 +3936,10 @@ static int read_partition_table(
        if (fseek(file, offset, SEEK_SET) < 0)
                error(1, errno, "Can not seek in the firmware");
 
-       if (fread(buf, 2048, 1, file) != 1)
+       if (fread(buf, sizeof(buf), 1, file) != 1)
                error(1, errno, "Can not read fwup-ptn from the firmware");
 
-       buf[2047] = '\0';
+       buf[sizeof(buf) - 1] = '\0';
 
        /* look for the partition header */
        if (memcmp(buf, parthdr, strlen(parthdr)) != 0) {
@@ -3790,6 +4015,67 @@ static int read_partition_table(
        return 0;
 }
 
+static void safeloader_read_partition(FILE *input_file, size_t payload_offset,
+                                     struct flash_partition_entry *entry,
+                                     struct image_partition_entry *part)
+{
+       size_t part_size = entry->size;
+       void *part_data = malloc(part_size);
+
+       if (fseek(input_file, payload_offset, SEEK_SET))
+               error(1, errno, "Failed to seek to partition data");
+
+       if (!part_data)
+               error(1, ENOMEM, "Failed to allocate partition data");
+
+       if (fread(part_data, 1, part_size, input_file) < part_size)
+               error(1, errno, "Failed to read partition data");
+
+       part->data = part_data;
+       part->size = part_size;
+       part->name = entry->name;
+}
+
+static void safeloader_parse_image(FILE *input_file, struct safeloader_image_info *image)
+{
+       static const char *HEADER_ID_CLOUD = "fw-type:Cloud";
+       static const char *HEADER_ID_QNEW = "?NEW";
+
+       char buf[64];
+
+       if (!input_file)
+               return;
+
+       fseek(input_file, SAFELOADER_PREAMBLE_SIZE, SEEK_SET);
+
+       if (fread(buf, sizeof(buf), 1, input_file) != 1)
+               error(1, errno, "Can not read image header");
+
+       if (memcmp(HEADER_ID_QNEW, &buf[0], strlen(HEADER_ID_QNEW)) == 0)
+               image->type = SAFELOADER_TYPE_QNEW;
+       else if (memcmp(HEADER_ID_CLOUD, &buf[0], strlen(HEADER_ID_CLOUD)) == 0)
+               image->type = SAFELOADER_TYPE_CLOUD;
+       else if (ntohl(*((uint32_t *) &buf[0])) <= SAFELOADER_HEADER_SIZE)
+               image->type = SAFELOADER_TYPE_VENDOR;
+       else
+               image->type = SAFELOADER_TYPE_DEFAULT;
+
+       switch (image->type) {
+       case SAFELOADER_TYPE_DEFAULT:
+       case SAFELOADER_TYPE_VENDOR:
+       case SAFELOADER_TYPE_CLOUD:
+               image->payload_offset = SAFELOADER_PAYLOAD_OFFSET;
+               break;
+       case SAFELOADER_TYPE_QNEW:
+               image->payload_offset = SAFELOADER_QNEW_PAYLOAD_OFFSET;
+               break;
+       }
+
+       /* Parse image partition table */
+       read_partition_table(input_file, image->payload_offset, &image->entries[0],
+                            MAX_PARTITIONS, PARTITION_TABLE_FWUP);
+}
+
 static void write_partition(
                FILE *input_file,
                size_t firmware_offset,
@@ -3839,12 +4125,9 @@ static int extract_firmware_partition(FILE *input_file, size_t firmware_offset,
 /** extract all partitions from the firmware file */
 static int extract_firmware(const char *input, const char *output_directory)
 {
-       struct flash_partition_entry entries[16] = { 0 };
-       size_t max_entries = 16;
-       size_t firmware_offset = 0x1014;
-       FILE *input_file;
-
+       struct safeloader_image_info info = {};
        struct stat statbuf;
+       FILE *input_file;
 
        /* check input file */
        if (stat(input, &statbuf)) {
@@ -3861,18 +4144,10 @@ static int extract_firmware(const char *input, const char *output_directory)
        }
 
        input_file = fopen(input, "rb");
+       safeloader_parse_image(input_file, &info);
 
-       if (read_partition_table(input_file, firmware_offset, entries, 16, PARTITION_TABLE_FWUP) != 0)
-               error(1, 0, "Error can not read the partition table (fwup-ptn)");
-
-       for (size_t i = 0; i < max_entries; i++) {
-               if (entries[i].name == NULL &&
-                               entries[i].base == 0 &&
-                               entries[i].size == 0)
-                       continue;
-
-               extract_firmware_partition(input_file, firmware_offset, &entries[i], output_directory);
-       }
+       for (size_t i = 0; i < MAX_PARTITIONS && info.entries[i].name; i++)
+               extract_firmware_partition(input_file, info.payload_offset, &info.entries[i], output_directory);
 
        return 0;
 }
@@ -3895,59 +4170,62 @@ static struct flash_partition_entry *find_partition(
 
 static int firmware_info(const char *input)
 {
-       struct flash_partition_entry pointers[MAX_PARTITIONS] = { };
+       struct safeloader_image_info info = {};
+       struct image_partition_entry part = {};
        struct flash_partition_entry *e;
-       FILE *fp;
-       int i;
+       FILE *input_file;
+
+       input_file = fopen(input, "rb");
 
-       fp = fopen(input, "r");
+       safeloader_parse_image(input_file, &info);
 
-       if (read_partition_table(fp, 0x1014, pointers, MAX_PARTITIONS, PARTITION_TABLE_FWUP))
-               error(1, 0, "Error can not read the partition table (fwup-ptn)");
+       if (info.type == SAFELOADER_TYPE_VENDOR) {
+               char buf[SAFELOADER_HEADER_SIZE] = {};
+               uint32_t vendor_size;
 
-       printf("Firmware image partitions:\n");
-       printf("%-8s %-8s %s\n", "base", "size", "name");
-       for (i = 0; i < MAX_PARTITIONS; i++) {
-               e = &pointers[i];
+               fseek(input_file, SAFELOADER_PREAMBLE_SIZE, SEEK_SET);
+               fread(&vendor_size, sizeof(uint32_t), 1, input_file);
 
-               if (!e->name && !e->base && !e->size)
-                       continue;
+               vendor_size = ntohl(vendor_size);
+               fread(buf, vendor_size, 1, input_file);
 
-               printf("%08x %08x %s\n", e->base, e->size, e->name ? e->name : "");
+               printf("Firmware vendor string:\n");
+               fwrite(buf, strnlen(buf, vendor_size), 1, stdout);
+               printf("\n");
        }
 
-       e = find_partition(pointers, MAX_PARTITIONS, "soft-version", NULL);
+       printf("Firmware image partitions:\n");
+       printf("%-8s %-8s %s\n", "base", "size", "name");
+
+       e = &info.entries[0];
+       for (unsigned int i = 0; i < MAX_PARTITIONS && e->name; i++, e++)
+               printf("%08x %08x %s\n", e->base, e->size, e->name);
+
+       e = find_partition(&info.entries[0], MAX_PARTITIONS, "soft-version", NULL);
        if (e) {
-               size_t data_len = e->size - sizeof(struct meta_header);
-               char *buf = malloc(data_len);
                struct soft_version *s;
+               unsigned int ascii_len;
+               const uint8_t *buf;
+               size_t data_len;
                bool isstr;
-               int i;
-
-               if (!buf)
-                       error(1, errno, "Failed to alloc buffer");
 
-               if (fseek(fp, 0x1014 + e->base + sizeof(struct meta_header), SEEK_SET))
-                       error(1, errno, "Can not seek in the firmware");
+               safeloader_read_partition(input_file, info.payload_offset + e->base, e, &part);
+               data_len = ntohl(((struct meta_header *) part.data)->length);
+               buf = part.data + sizeof(struct meta_header);
 
-               if (fread(buf, data_len, 1, fp) != 1)
-                       error(1, errno, "Can not read fwup-ptn data from the firmware");
+               /* Check for (null-terminated) string */
+               ascii_len = 0;
+               while (ascii_len < data_len && isascii(buf[ascii_len]))
+                       ascii_len++;
 
-               /* Check for string ignoring padding character */
-               isstr = true;
-               for (i = 0; i < data_len - 1; i++) {
-                       if (!isascii(buf[i])) {
-                               isstr = false;
-                               break;
-                       }
-               }
+               isstr = ascii_len == data_len;
 
                printf("\n[Software version]\n");
                if (isstr) {
-                       fwrite(buf, data_len, 1, stdout);
+                       fwrite(buf, strnlen((const char *) buf, data_len), 1, stdout);
                        putchar('\n');
                } else if (data_len >= offsetof(struct soft_version, rev)) {
-                       s = (struct soft_version *)buf;
+                       s = (struct soft_version *) buf;
 
                        printf("Version: %d.%d.%d\n", s->version_major, s->version_minor, s->version_patch);
                        printf("Date: %02x%02x-%02x-%02x\n", s->year_hi, s->year_lo, s->month, s->day);
@@ -3956,52 +4234,40 @@ static int firmware_info(const char *input)
                        printf("Failed to parse data\n");
                }
 
-               free(buf);
+               free_image_partition(&part);
        }
 
-       e = find_partition(pointers, MAX_PARTITIONS, "support-list", NULL);
+       e = find_partition(&info.entries[0], MAX_PARTITIONS, "support-list", NULL);
        if (e) {
-               char buf[128];
-               size_t length;
-               size_t bytes;
-               size_t max_length = sizeof(buf) - 1;
+               size_t data_len;
 
-               if (fseek(fp, 0x1014 + e->base + sizeof(struct meta_header), SEEK_SET))
-                       error(1, errno, "Can not seek in the firmware");
+               safeloader_read_partition(input_file, info.payload_offset + e->base, e, &part);
+               data_len = ntohl(((struct meta_header *) part.data)->length);
 
                printf("\n[Support list]\n");
-               for (length = e->size - sizeof(struct meta_header); length; length -= bytes) {
-                       bytes = fread(buf, 1, length > max_length ? max_length: length, fp);
-                       if (bytes <= 0)
-                               error(1, errno, "Can not read fwup-ptn data from the firmware");
+               fwrite(part.data + sizeof(struct meta_header), data_len, 1, stdout);
+               printf("\n");
 
-                       buf[bytes] = '\0';
-                       printf(buf);
-               }
-        printf("\n");
+               free_image_partition(&part);
        }
 
-       e = find_partition(pointers, MAX_PARTITIONS, "partition-table", NULL);
+       e = find_partition(&info.entries[0], MAX_PARTITIONS, "partition-table", NULL);
        if (e) {
-               size_t flash_table_offset = 0x1014 + e->base + 4;
-               struct flash_partition_entry parts[MAX_PARTITIONS] = { };
+               size_t flash_table_offset = info.payload_offset + e->base + 4;
+               struct flash_partition_entry parts[MAX_PARTITIONS] = {};
 
-               if (read_partition_table(fp, flash_table_offset, parts, MAX_PARTITIONS, PARTITION_TABLE_FLASH))
+               if (read_partition_table(input_file, flash_table_offset, parts, MAX_PARTITIONS, PARTITION_TABLE_FLASH))
                        error(1, 0, "Error can not read the partition table (partition)");
 
                printf("\n[Partition table]\n");
                printf("%-8s %-8s %s\n", "base", "size", "name");
-               for (i = 0; i < MAX_PARTITIONS; i++) {
-                       e = &parts[i];
 
-                       if (!e->name && !e->base && !e->size)
-                               continue;
-
-                       printf("%08x %08x %s\n", e->base, e->size, e->name ? e->name : "");
-               }
+               e = &parts[0];
+               for (unsigned int i = 0; i < MAX_PARTITIONS && e->name; i++, e++)
+                       printf("%08x %08x %s\n", e->base, e->size, e->name);
        }
 
-       fclose(fp);
+       fclose(input_file);
 
        return 0;
 }
@@ -4028,16 +4294,17 @@ static void write_ff(FILE *output_file, size_t size)
 
 static void convert_firmware(const char *input, const char *output)
 {
-       struct flash_partition_entry fwup[MAX_PARTITIONS] = { 0 };
-       struct flash_partition_entry flash[MAX_PARTITIONS] = { 0 };
-       struct flash_partition_entry *fwup_os_image = NULL, *fwup_file_system = NULL;
-       struct flash_partition_entry *flash_os_image = NULL, *flash_file_system = NULL;
-       struct flash_partition_entry *fwup_partition_table = NULL;
-       size_t firmware_offset = 0x1014;
-       FILE *input_file, *output_file;
+       struct flash_partition_entry flash[MAX_PARTITIONS] = {};
+       struct flash_partition_entry *fwup_partition_table;
+       struct flash_partition_entry *flash_file_system;
+       struct flash_partition_entry *fwup_file_system;
+       struct flash_partition_entry *flash_os_image;
+       struct flash_partition_entry *fwup_os_image;
+       struct safeloader_image_info info = {};
        size_t flash_table_offset;
-
        struct stat statbuf;
+       FILE *output_file;
+       FILE *input_file;
 
        /* check input file */
        if (stat(input, &statbuf)) {
@@ -4052,19 +4319,18 @@ static void convert_firmware(const char *input, const char *output)
        if (!output_file)
                error(1, 0, "Can not open output firmware %s", output);
 
-       if (read_partition_table(input_file, firmware_offset, fwup, MAX_PARTITIONS, 0) != 0) {
-               error(1, 0, "Error can not read the partition table (fwup-ptn)");
-       }
+       input_file = fopen(input, "rb");
+       safeloader_parse_image(input_file, &info);
 
-       fwup_os_image = find_partition(fwup, MAX_PARTITIONS,
+       fwup_os_image = find_partition(info.entries, MAX_PARTITIONS,
                        "os-image", "Error can not find os-image partition (fwup)");
-       fwup_file_system = find_partition(fwup, MAX_PARTITIONS,
+       fwup_file_system = find_partition(info.entries, MAX_PARTITIONS,
                        "file-system", "Error can not find file-system partition (fwup)");
-       fwup_partition_table = find_partition(fwup, MAX_PARTITIONS,
+       fwup_partition_table = find_partition(info.entries, MAX_PARTITIONS,
                        "partition-table", "Error can not find partition-table partition");
 
        /* the flash partition table has a 0x00000004 magic haeder */
-       flash_table_offset = firmware_offset + fwup_partition_table->base + 4;
+       flash_table_offset = info.payload_offset + fwup_partition_table->base + 4;
        if (read_partition_table(input_file, flash_table_offset, flash, MAX_PARTITIONS, PARTITION_TABLE_FLASH) != 0)
                error(1, 0, "Error can not read the partition table (flash)");
 
@@ -4074,13 +4340,12 @@ static void convert_firmware(const char *input, const char *output)
                        "file-system", "Error can not find file-system partition (flash)");
 
        /* write os_image to 0x0 */
-       write_partition(input_file, firmware_offset, fwup_os_image, output_file);
+       write_partition(input_file, info.payload_offset, fwup_os_image, output_file);
        write_ff(output_file, flash_os_image->size - fwup_os_image->size);
 
        /* write file-system behind os_image */
        fseek(output_file, flash_file_system->base - flash_os_image->base, SEEK_SET);
-       write_partition(input_file, firmware_offset, fwup_file_system, output_file);
-       write_ff(output_file, flash_file_system->size - fwup_file_system->size);
+       write_partition(input_file, info.payload_offset, fwup_file_system, output_file);
 
        fclose(output_file);
        fclose(input_file);