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 <sander@svanheule.net>
This commit is contained in:
		 Sander Vanheule
					Sander Vanheule
				
			
				
					committed by
					
						 Alexander Couzens
						Alexander Couzens
					
				
			
			
				
	
			
			
			 Alexander Couzens
						Alexander Couzens
					
				
			
						parent
						
							588933ae9a
						
					
				
				
					commit
					1a211af2cb
				
			| @@ -7,7 +7,7 @@ | |||||||
| include $(TOPDIR)/rules.mk | include $(TOPDIR)/rules.mk | ||||||
|  |  | ||||||
| PKG_NAME := firmware-utils | PKG_NAME := firmware-utils | ||||||
| PKG_RELEASE := 5 | PKG_RELEASE := 6 | ||||||
|  |  | ||||||
| include $(INCLUDE_DIR)/host-build.mk | include $(INCLUDE_DIR)/host-build.mk | ||||||
| include $(INCLUDE_DIR)/kernel.mk | include $(INCLUDE_DIR)/kernel.mk | ||||||
|   | |||||||
| @@ -83,10 +83,13 @@ struct device_info { | |||||||
| 	const char *last_sysupgrade_partition; | 	const char *last_sysupgrade_partition; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct __attribute__((__packed__)) meta_header { | ||||||
|  | 	uint32_t length; | ||||||
|  | 	uint32_t zero; | ||||||
|  | }; | ||||||
|  |  | ||||||
| /** The content of the soft-version structure */ | /** The content of the soft-version structure */ | ||||||
| struct __attribute__((__packed__)) soft_version { | struct __attribute__((__packed__)) soft_version { | ||||||
| 	uint32_t data_len; |  | ||||||
| 	uint32_t zero; |  | ||||||
| 	uint8_t pad1; | 	uint8_t pad1; | ||||||
| 	uint8_t version_major; | 	uint8_t version_major; | ||||||
| 	uint8_t version_minor; | 	uint8_t version_minor; | ||||||
| @@ -96,6 +99,7 @@ struct __attribute__((__packed__)) soft_version { | |||||||
| 	uint8_t month; | 	uint8_t month; | ||||||
| 	uint8_t day; | 	uint8_t day; | ||||||
| 	uint32_t rev; | 	uint32_t rev; | ||||||
|  | 	uint32_t compat_level; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -2299,6 +2303,35 @@ static inline void put32(uint8_t *buf, uint32_t val) { | |||||||
| 	buf[3] = 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 */ | /** Allocates a new image partition */ | ||||||
| static struct image_partition_entry alloc_image_partition(const char *name, size_t len) { | static struct image_partition_entry alloc_image_partition(const char *name, size_t len) { | ||||||
| 	struct image_partition_entry entry = {name, len, malloc(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 */ | /** Generates the soft-version partition */ | ||||||
| static struct image_partition_entry make_soft_version(struct device_info *info, uint32_t rev) { | static struct image_partition_entry make_soft_version( | ||||||
| 	size_t part_len = sizeof(struct soft_version); | 	const struct device_info *info, uint32_t rev) | ||||||
| 	if (info->soft_ver_compat_level > 0) | { | ||||||
| 		part_len += sizeof(uint32_t); | 	/** If an info string is provided, use this instead of | ||||||
|  | 	 * the structured data, and include the null-termination */ | ||||||
| 	struct image_partition_entry entry = | 	if (info->soft_ver) { | ||||||
| 	    alloc_image_partition("soft-version", part_len+1); | 		uint32_t len = strlen(info->soft_ver) + 1; | ||||||
| 	struct soft_version *s = (struct soft_version *)entry.data; | 		return init_meta_partition_entry("soft-version", | ||||||
|  | 			info->soft_ver, len, 0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	time_t t; | 	time_t t; | ||||||
|  |  | ||||||
| @@ -2382,58 +2417,43 @@ static struct image_partition_entry make_soft_version(struct device_info *info, | |||||||
|  |  | ||||||
| 	struct tm *tm = gmtime(&t); | 	struct tm *tm = gmtime(&t); | ||||||
|  |  | ||||||
| 	/* Partition contents size, minus 8 byte header and trailing byte */ | 	struct soft_version s = { | ||||||
| 	s->data_len = htonl(entry.size-9); | 		.pad1 = 0xff, | ||||||
| 	s->zero = 0; |  | ||||||
| 	s->pad1 = 0xff; |  | ||||||
|  |  | ||||||
| 	s->version_major = 0; | 		.version_major = 0, | ||||||
| 	s->version_minor = 0; | 		.version_minor = 0, | ||||||
| 	s->version_patch = 0; | 		.version_patch = 0, | ||||||
|  |  | ||||||
| 	s->year_hi = bcd((1900+tm->tm_year)/100); | 		.year_hi = bcd((1900+tm->tm_year)/100), | ||||||
| 	s->year_lo = bcd(tm->tm_year%100); | 		.year_lo = bcd(tm->tm_year%100), | ||||||
| 	s->month = bcd(tm->tm_mon+1); | 		.month = bcd(tm->tm_mon+1), | ||||||
| 	s->day = bcd(tm->tm_mday); | 		.day = bcd(tm->tm_mday), | ||||||
| 	s->rev = htonl(rev); |  | ||||||
|  |  | ||||||
| 	if (info->soft_ver_compat_level > 0) | 		.compat_level = htonl(info->soft_ver_compat_level) | ||||||
| 		*(uint32_t *)(entry.data + sizeof(struct soft_version)) = | 	}; | ||||||
| 		    htonl(info->soft_ver_compat_level); |  | ||||||
|  |  | ||||||
| 	entry.data[entry.size-1] = 0xff; | 	if (info->soft_ver_compat_level == 0) | ||||||
|  | 		return init_meta_partition_entry("soft-version", &s, | ||||||
| 	return entry; | 			(uint8_t *)(&s.compat_level) - (uint8_t *)(&s), 0xff); | ||||||
| } | 	else | ||||||
|  | 		return init_meta_partition_entry("soft-version", &s, | ||||||
| static struct image_partition_entry make_soft_version_from_string(const char *soft_ver) { | 			sizeof(s), 0xff); | ||||||
| 	/** 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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** Generates the support-list partition */ | /** Generates the support-list partition */ | ||||||
| static struct image_partition_entry make_support_list(struct device_info *info) { | static struct image_partition_entry make_support_list( | ||||||
| 	size_t len = strlen(info->support_list); | 	const struct device_info *info) | ||||||
| 	struct image_partition_entry entry = alloc_image_partition("support-list", len + 9); | { | ||||||
|  | 	uint32_t len = strlen(info->support_list); | ||||||
|  | 	return init_meta_partition_entry("support-list", info->support_list, | ||||||
|  | 		len, info->support_trail); | ||||||
|  | } | ||||||
|  |  | ||||||
| 	put32(entry.data, len); | /** Partition with extra-para data */ | ||||||
| 	memset(entry.data+4, 0, 4); | static struct image_partition_entry make_extra_para( | ||||||
| 	memcpy(entry.data+8, info->support_list, len); | 	const struct device_info *info, const uint8_t *extra_para, size_t len) | ||||||
| 	entry.data[len+8] = info->support_trail; | { | ||||||
|  | 	return init_meta_partition_entry("extra-para", extra_para, len, 0x00); | ||||||
| 	return entry; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** Creates a new image partition with an arbitrary name from a file */ | /** 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; | 	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 |    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); | 	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[2] = make_support_list(info); | ||||||
| 	parts[3] = read_file("os-image", kernel_image, false, NULL); | 	parts[3] = read_file("os-image", kernel_image, false, NULL); | ||||||
| 	parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof, file_system_partition); | 	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 */ | 	/* 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-C25-V1") == 0 || | ||||||
| 	    strcasecmp(info->id, "ARCHER-C59-V2") == 0 || | 	    strcasecmp(info->id, "ARCHER-C59-V2") == 0 || | ||||||
| 	    strcasecmp(info->id, "ARCHER-C60-V2") == 0 || | 	    strcasecmp(info->id, "ARCHER-C60-V2") == 0 || | ||||||
| 	    strcasecmp(info->id, "ARCHER-C60-V3") == 0 || | 	    strcasecmp(info->id, "ARCHER-C60-V3") == 0 || | ||||||
| 	    strcasecmp(info->id, "TLWR1043NV5") == 0) { | 	    strcasecmp(info->id, "TLWR1043NV5") == 0) { | ||||||
| 		const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}; | 		const uint8_t extra_para[2] = {0x01, 0x00}; | ||||||
| 		parts[5] = put_data("extra-para", mdat, 11); | 		parts[5] = make_extra_para(info, extra_para, | ||||||
| 	} else if (strcasecmp(info->id, "ARCHER-A7-V5") == 0 || strcasecmp(info->id, "ARCHER-C7-V4") == 0 || strcasecmp(info->id, "ARCHER-C7-V5") == 0) { | 			sizeof(extra_para)); | ||||||
| 		const char mdat[11] = {0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x01, 0x00, 0x00}; |  | ||||||
| 		parts[5] = put_data("extra-para", mdat, 11); |  | ||||||
| 	} else if (strcasecmp(info->id, "ARCHER-C6-V2") == 0) { | 	} else if (strcasecmp(info->id, "ARCHER-C6-V2") == 0) { | ||||||
| 		const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}; | 		const uint8_t extra_para[2] = {0x00, 0x01}; | ||||||
| 		parts[5] = put_data("extra-para", mdat, 11); | 		parts[5] = make_extra_para(info, extra_para, | ||||||
| 	} else if (strcasecmp(info->id, "ARCHER-C6-V2-US") == 0) { | 			sizeof(extra_para)); | ||||||
| 		const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}; | 	} else if (strcasecmp(info->id, "ARCHER-C6-V2-US") == 0 || | ||||||
| 		parts[5] = put_data("extra-para", mdat, 11); | 		   strcasecmp(info->id, "EAP245-V3") == 0) { | ||||||
| 	} else if (strcasecmp(info->id, "EAP245-V3") == 0) { | 		const uint8_t extra_para[2] = {0x01, 0x01}; | ||||||
| 		const char mdat[10] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}; | 		parts[5] = make_extra_para(info, extra_para, | ||||||
| 		parts[5] = put_data("extra-para", mdat, 10); | 			sizeof(extra_para)); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	size_t len; | 	size_t len; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user