From 1a211af2cb3be415a2c3454dea6fdc9a59bba334 Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Wed, 5 Aug 2020 20:49:44 +0200 Subject: [PATCH] firmware-utils: tplink-safeloader: refactor meta-partition generation TP-Link safeloader firmware images contain a number of (small) partitions with information about the device. These consist of: * The data length as a 32-bit integer * A 32-bit zero padding * The partition data, with its length set in the first field The OpenWrt factory image partitions that follow this structure are soft-version, support-list, and extra-para. Refactor the code to put all common logic into one allocation call, and let the rest of the data be filled in by the original functions. Due to the extra-para changes, this patch results in factory images that change by 2 bytes (not counting the checksum) for three devices: * ARCHER-A7-V5 * ARCHER-C7-V4 * ARCHER-C7-V5 These were the devices where the extra-para blob didn't match the common format. The hardcoded data also didn't correspond to TP-Link's (recent) upgrade images, which actually matches the meta-partition format. A padding byte is also added to the extra-para partition for EAP245-V3. Signed-off-by: Sander Vanheule --- tools/firmware-utils/Makefile | 2 +- tools/firmware-utils/src/tplink-safeloader.c | 173 ++++++++++--------- 2 files changed, 91 insertions(+), 84 deletions(-) diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 81c62d977a..9c9a0ba40f 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME := firmware-utils -PKG_RELEASE := 5 +PKG_RELEASE := 6 include $(INCLUDE_DIR)/host-build.mk include $(INCLUDE_DIR)/kernel.mk diff --git a/tools/firmware-utils/src/tplink-safeloader.c b/tools/firmware-utils/src/tplink-safeloader.c index cb3cf69948..41b3b8aff9 100644 --- a/tools/firmware-utils/src/tplink-safeloader.c +++ b/tools/firmware-utils/src/tplink-safeloader.c @@ -83,10 +83,13 @@ struct device_info { const char *last_sysupgrade_partition; }; +struct __attribute__((__packed__)) meta_header { + uint32_t length; + uint32_t zero; +}; + /** The content of the soft-version structure */ struct __attribute__((__packed__)) soft_version { - uint32_t data_len; - uint32_t zero; uint8_t pad1; uint8_t version_major; uint8_t version_minor; @@ -96,6 +99,7 @@ struct __attribute__((__packed__)) soft_version { uint8_t month; uint8_t day; uint32_t rev; + uint32_t compat_level; }; @@ -2299,6 +2303,35 @@ static inline void put32(uint8_t *buf, uint32_t val) { buf[3] = val; } +/** Allocate a padded meta partition with a correctly initialised header + * If the `data` pointer is NULL, then the required space is only allocated, + * otherwise `data_len` bytes will be copied from `data` into the partition + * entry. */ +static struct image_partition_entry init_meta_partition_entry( + const char *name, const void *data, uint32_t data_len, + uint8_t pad_value) +{ + uint32_t total_len = sizeof(struct meta_header) + data_len + 1; + struct image_partition_entry entry = { + .name = name, + .size = total_len, + .data = malloc(total_len) + }; + if (!entry.data) + error(1, errno, "failed to allocate meta partition entry"); + + struct meta_header *header = (struct meta_header *)entry.data; + header->length = htonl(data_len); + header->zero = 0; + + if (data) + memcpy(entry.data+sizeof(*header), data, data_len); + + entry.data[total_len - 1] = pad_value; + + return entry; +} + /** Allocates a new image partition */ static struct image_partition_entry alloc_image_partition(const char *name, size_t len) { struct image_partition_entry entry = {name, len, malloc(len)}; @@ -2364,14 +2397,16 @@ static inline uint8_t bcd(uint8_t v) { /** Generates the soft-version partition */ -static struct image_partition_entry make_soft_version(struct device_info *info, uint32_t rev) { - size_t part_len = sizeof(struct soft_version); - if (info->soft_ver_compat_level > 0) - part_len += sizeof(uint32_t); - - struct image_partition_entry entry = - alloc_image_partition("soft-version", part_len+1); - struct soft_version *s = (struct soft_version *)entry.data; +static struct image_partition_entry make_soft_version( + const struct device_info *info, uint32_t rev) +{ + /** If an info string is provided, use this instead of + * the structured data, and include the null-termination */ + if (info->soft_ver) { + uint32_t len = strlen(info->soft_ver) + 1; + return init_meta_partition_entry("soft-version", + info->soft_ver, len, 0); + } time_t t; @@ -2382,58 +2417,43 @@ static struct image_partition_entry make_soft_version(struct device_info *info, struct tm *tm = gmtime(&t); - /* Partition contents size, minus 8 byte header and trailing byte */ - s->data_len = htonl(entry.size-9); - s->zero = 0; - s->pad1 = 0xff; - - s->version_major = 0; - s->version_minor = 0; - s->version_patch = 0; - - s->year_hi = bcd((1900+tm->tm_year)/100); - s->year_lo = bcd(tm->tm_year%100); - s->month = bcd(tm->tm_mon+1); - s->day = bcd(tm->tm_mday); - s->rev = htonl(rev); + struct soft_version s = { + .pad1 = 0xff, - if (info->soft_ver_compat_level > 0) - *(uint32_t *)(entry.data + sizeof(struct soft_version)) = - htonl(info->soft_ver_compat_level); + .version_major = 0, + .version_minor = 0, + .version_patch = 0, - entry.data[entry.size-1] = 0xff; + .year_hi = bcd((1900+tm->tm_year)/100), + .year_lo = bcd(tm->tm_year%100), + .month = bcd(tm->tm_mon+1), + .day = bcd(tm->tm_mday), - return entry; -} + .compat_level = htonl(info->soft_ver_compat_level) + }; -static struct image_partition_entry make_soft_version_from_string(const char *soft_ver) { - /** String length _including_ the terminating zero byte */ - uint32_t ver_len = strlen(soft_ver) + 1; - /** Partition contains 64 bit header, the version string, and one additional null byte */ - size_t partition_len = 2*sizeof(uint32_t) + ver_len + 1; - struct image_partition_entry entry = alloc_image_partition("soft-version", partition_len); - - uint32_t *len = (uint32_t *)entry.data; - len[0] = htonl(ver_len); - len[1] = 0; - memcpy(&len[2], soft_ver, ver_len); - - entry.data[partition_len - 1] = 0; - - return entry; + if (info->soft_ver_compat_level == 0) + return init_meta_partition_entry("soft-version", &s, + (uint8_t *)(&s.compat_level) - (uint8_t *)(&s), 0xff); + else + return init_meta_partition_entry("soft-version", &s, + sizeof(s), 0xff); } /** Generates the support-list partition */ -static struct image_partition_entry make_support_list(struct device_info *info) { - size_t len = strlen(info->support_list); - struct image_partition_entry entry = alloc_image_partition("support-list", len + 9); - - put32(entry.data, len); - memset(entry.data+4, 0, 4); - memcpy(entry.data+8, info->support_list, len); - entry.data[len+8] = info->support_trail; +static struct image_partition_entry make_support_list( + const struct device_info *info) +{ + uint32_t len = strlen(info->support_list); + return init_meta_partition_entry("support-list", info->support_list, + len, info->support_trail); +} - return entry; +/** Partition with extra-para data */ +static struct image_partition_entry make_extra_para( + const struct device_info *info, const uint8_t *extra_para, size_t len) +{ + return init_meta_partition_entry("extra-para", extra_para, len, 0x00); } /** Creates a new image partition with an arbitrary name from a file */ @@ -2473,16 +2493,6 @@ static struct image_partition_entry read_file(const char *part_name, const char return entry; } -/** Creates a new image partition from arbitrary data */ -static struct image_partition_entry put_data(const char *part_name, const char *datain, size_t len) { - - struct image_partition_entry entry = alloc_image_partition(part_name, len); - - memcpy(entry.data, datain, len); - - return entry; -} - /** Copies a list of image partitions into an image buffer and generates the image partition table while doing so @@ -2710,36 +2720,33 @@ static void build_image(const char *output, } parts[0] = make_partition_table(info->partitions); - if (info->soft_ver) - parts[1] = make_soft_version_from_string(info->soft_ver); - else - parts[1] = make_soft_version(info, rev); - + parts[1] = make_soft_version(info, rev); parts[2] = make_support_list(info); parts[3] = read_file("os-image", kernel_image, false, NULL); parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof, file_system_partition); /* Some devices need the extra-para partition to accept the firmware */ - if (strcasecmp(info->id, "ARCHER-C2-V3") == 0 || + if (strcasecmp(info->id, "ARCHER-A7-V5") == 0 || + strcasecmp(info->id, "ARCHER-C2-V3") == 0 || + strcasecmp(info->id, "ARCHER-C7-V4") == 0 || + strcasecmp(info->id, "ARCHER-C7-V5") == 0 || strcasecmp(info->id, "ARCHER-C25-V1") == 0 || strcasecmp(info->id, "ARCHER-C59-V2") == 0 || strcasecmp(info->id, "ARCHER-C60-V2") == 0 || strcasecmp(info->id, "ARCHER-C60-V3") == 0 || strcasecmp(info->id, "TLWR1043NV5") == 0) { - const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}; - parts[5] = put_data("extra-para", mdat, 11); - } else if (strcasecmp(info->id, "ARCHER-A7-V5") == 0 || strcasecmp(info->id, "ARCHER-C7-V4") == 0 || strcasecmp(info->id, "ARCHER-C7-V5") == 0) { - const char mdat[11] = {0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x01, 0x00, 0x00}; - parts[5] = put_data("extra-para", mdat, 11); + const uint8_t extra_para[2] = {0x01, 0x00}; + parts[5] = make_extra_para(info, extra_para, + sizeof(extra_para)); } else if (strcasecmp(info->id, "ARCHER-C6-V2") == 0) { - const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}; - parts[5] = put_data("extra-para", mdat, 11); - } else if (strcasecmp(info->id, "ARCHER-C6-V2-US") == 0) { - const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}; - parts[5] = put_data("extra-para", mdat, 11); - } else if (strcasecmp(info->id, "EAP245-V3") == 0) { - const char mdat[10] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}; - parts[5] = put_data("extra-para", mdat, 10); + const uint8_t extra_para[2] = {0x00, 0x01}; + parts[5] = make_extra_para(info, extra_para, + sizeof(extra_para)); + } else if (strcasecmp(info->id, "ARCHER-C6-V2-US") == 0 || + strcasecmp(info->id, "EAP245-V3") == 0) { + const uint8_t extra_para[2] = {0x01, 0x01}; + parts[5] = make_extra_para(info, extra_para, + sizeof(extra_para)); } size_t len; -- 2.30.2