[OpenWrt-Devel] [PATCH 2/4] firmware-utils: add support for TP-Link RE450 for tplink-safeload

Tal Keren kooolk at gmail.com
Wed Jan 6 19:40:46 EST 2016


Change tplink-safeloader to make it simpler to support new devices.
This design seems to fit for all the devices seen so far (CPE510/RE450/Archer
C2600 and similiar devices).
Add check for partition size when creating factory image.

Add the firmware layout of RE450. The firmware layout is different from the
stock layout, since the stock layout didn't have enough space for the kernel.
512KB were moved from the file system to the kerenl partition.
This will work with the OEM firmware since it uses the partition table
specified in the new firmware when flashing it.

Signed-off-by: Tal Keren <kooolk at gmail.com>
---
 tools/firmware-utils/src/tplink-safeloader.c | 290 ++++++++++++++++++---------
 1 file changed, 195 insertions(+), 95 deletions(-)

diff --git a/tools/firmware-utils/src/tplink-safeloader.c b/tools/firmware-utils/src/tplink-safeloader.c
index 77a894b..fb7401f 100644
--- a/tools/firmware-utils/src/tplink-safeloader.c
+++ b/tools/firmware-utils/src/tplink-safeloader.c
@@ -27,8 +27,7 @@
 /*
    tplink-safeloader
 
-   Image generation tool for the TP-LINK SafeLoader as seen on
-   TP-LINK Pharos devices (CPE210/220/510/520)
+   Image generation tool for the TP-LINK SafeLoader
 */
 
 
@@ -53,6 +52,8 @@
 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
 
 
+#define MAX_PARTITIONS	32
+
 /** An image partition table entry */
 struct image_partition_entry {
 	const char *name;
@@ -67,6 +68,16 @@ struct flash_partition_entry {
 	uint32_t size;
 };
 
+/** Information requried to build the firmware */
+struct firmware_info {
+	const char *id;
+	const struct flash_partition_entry flash_partitions[MAX_PARTITIONS+1];
+	const char *vendor;
+	const char *support_list;
+	const char *first_sysupgrade_partition;
+	const char *last_sysupgrade_partition;
+};
+
 
 /** The content of the soft-version structure */
 struct __attribute__((__packed__)) soft_version {
@@ -102,45 +113,91 @@ static const uint8_t md5_salt[16] = {
 };
 
 
-/** Vendor information for CPE210/220/510/520 */
-static const char cpe510_vendor[] = "CPE510(TP-LINK|UN|N300-5):1.0\r\n";
-
-
-/**
-    The flash partition table for CPE210/220/510/520;
-    it is the same as the one used by the stock images.
-*/
-static const struct flash_partition_entry cpe510_partitions[] = {
-	{"fs-uboot", 0x00000, 0x20000},
-	{"partition-table", 0x20000, 0x02000},
-	{"default-mac", 0x30000, 0x00020},
-	{"product-info", 0x31100, 0x00100},
-	{"signature", 0x32000, 0x00400},
-	{"os-image", 0x40000, 0x170000},
-	{"soft-version", 0x1b0000, 0x00100},
-	{"support-list", 0x1b1000, 0x00400},
-	{"file-system", 0x1c0000, 0x600000},
-	{"user-config", 0x7c0000, 0x10000},
-	{"default-config", 0x7d0000, 0x10000},
-	{"log", 0x7e0000, 0x10000},
-	{"radio", 0x7f0000, 0x10000},
-	{NULL, 0, 0}
+static struct firmware_info boards[] = {
+	{
+		.id	= "CPE510",
+
+		/**
+		   The flash partition table for CPE210/220/510/520;
+		   it is the same as the one used by the stock images.
+		*/
+		.flash_partitions = {
+			{"fs-uboot", 0x00000, 0x20000},
+			{"partition-table", 0x20000, 0x02000},
+			{"default-mac", 0x30000, 0x00020},
+			{"product-info", 0x31100, 0x00100},
+			{"signature", 0x32000, 0x00400},
+			{"os-image", 0x40000, 0x170000},
+			{"soft-version", 0x1b0000, 0x00100},
+			{"support-list", 0x1b1000, 0x00400},
+			{"file-system", 0x1c0000, 0x600000},
+			{"user-config", 0x7c0000, 0x10000},
+			{"default-config", 0x7d0000, 0x10000},
+			{"log", 0x7e0000, 0x10000},
+			{"radio", 0x7f0000, 0x10000},
+			{NULL, 0, 0}
+		},
+
+		/** Vendor information for CPE210/220/510/520 */
+		.vendor	= "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
+
+		/**
+		   The support list for RE450
+		*/
+		.support_list =
+			"SupportList:\r\n"
+			"CPE510(TP-LINK|UN|N300-5):1.0\r\n"
+			"CPE510(TP-LINK|UN|N300-5):1.1\r\n"
+			"CPE520(TP-LINK|UN|N300-5):1.0\r\n"
+			"CPE520(TP-LINK|UN|N300-5):1.1\r\n"
+			"CPE210(TP-LINK|UN|N300-2):1.0\r\n"
+			"CPE210(TP-LINK|UN|N300-2):1.1\r\n"
+			"CPE220(TP-LINK|UN|N300-2):1.0\r\n"
+			"CPE220(TP-LINK|UN|N300-2):1.1\r\n",
+
+		.first_sysupgrade_partition = "os-image",
+
+		.last_sysupgrade_partition = "file-system",
+	}, {
+		.id	= "RE450",
+
+		/**
+		   The flash partition table for RE450;
+		   it is almost the same as the one used by the stock images,
+		   512KB were moved from file-system to os-image.
+		*/
+		.flash_partitions = {
+			{"fs-uboot", 0x00000, 0x20000},
+			{"os-image", 0x20000, 0x140000},
+			{"file-system", 0x160000, 0x4a0000},
+			{"partition-table", 0x600000, 0x02000},
+			{"default-mac", 0x610000, 0x00020},
+			{"pin", 0x610100, 0x00020},
+			{"product-info", 0x611100, 0x01000},
+			{"soft-version", 0x620000, 0x01000},
+			{"support-list", 0x621000, 0x01000},
+			{"profile", 0x622000, 0x08000},
+			{"user-config", 0x630000, 0x10000},
+			{"default-config", 0x640000, 0x10000},
+			{"radio", 0x7f0000, 0x10000},
+			{NULL, 0, 0}
+		},
+
+		/**
+		   The support list for RE450
+		*/
+		.support_list =
+			"SupportList:\n"
+			"{product_name:RE450,product_ver:1.0.0,special_id:00000000}\n",
+
+		.first_sysupgrade_partition = "os-image",
+
+		.last_sysupgrade_partition = "file-system",
+	}, {
+		/* terminating entry */
+	}
 };
 
-/**
-   The support list for CPE210/220/510/520
-*/
-static const char cpe510_support_list[] =
-	"SupportList:\r\n"
-	"CPE510(TP-LINK|UN|N300-5):1.0\r\n"
-	"CPE510(TP-LINK|UN|N300-5):1.1\r\n"
-	"CPE520(TP-LINK|UN|N300-5):1.0\r\n"
-	"CPE520(TP-LINK|UN|N300-5):1.1\r\n"
-	"CPE210(TP-LINK|UN|N300-2):1.0\r\n"
-	"CPE210(TP-LINK|UN|N300-2):1.1\r\n"
-	"CPE220(TP-LINK|UN|N300-2):1.0\r\n"
-	"CPE220(TP-LINK|UN|N300-2):1.1\r\n";
-
 #define error(_ret, _errno, _str, ...)				\
 	do {							\
 		fprintf(stderr, _str ": %s\n", ## __VA_ARGS__,	\
@@ -313,12 +370,22 @@ static struct image_partition_entry read_file(const char *part_name, const char
 
    I think partition-table must be the first partition in the firmware image.
 */
-static void put_partitions(uint8_t *buffer, const struct image_partition_entry *parts) {
-	size_t i;
+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;
 
 	size_t base = 0x800;
 	for (i = 0; parts[i].name; i++) {
+		for (j = 0; flash_parts[j].name; j++) {
+			if (!strcmp(flash_parts[j].name, parts[i].name)) {
+				if (parts[i].size > flash_parts[j].size)
+					error(1, 0, "%s partition too big (more than %u bytes)", flash_parts[j].name, (unsigned)flash_parts[j].size);
+				break;
+			}
+		}
+		/** Make sure that partition found */
+		assert(flash_parts[j].name);
+
 		memcpy(buffer + base, parts[i].data, parts[i].size);
 
 		size_t len = end-image_pt;
@@ -333,8 +400,6 @@ static void put_partitions(uint8_t *buffer, const struct image_partition_entry *
 	}
 
 	image_pt++;
-
-	memset(image_pt, 0xff, end-image_pt);
 }
 
 /** Generates and writes the image MD5 checksum */
@@ -357,13 +422,18 @@ static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
      -----------  -----
      0000-0003    Image size (4 bytes, big endian)
      0004-0013    MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
-     0014-0017    Vendor information length (without padding) (4 bytes, big endian)
-     0018-1013    Vendor information (4092 bytes, padded with 0xff; there seem to be older
+
+     If have vendor information: (Not included in all devices)
+        0014-0018    Vendorm information length (without padding) (4 bytes, big endian)
+        0018-1013    Vendor information (4092 bytes, padded with 0xff; there seem to be older
                   (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
+     Else:
+        0014-1013    0xff padding
+
      1014-1813    Image partition table (2048 bytes, padded with 0xff)
      1814-xxxx    Firmware partitions
 */
-static void * generate_factory_image(const char *vendor, const struct image_partition_entry *parts, size_t *len) {
+static void * generate_factory_image(const char *vendor, const struct flash_partition_entry *flash_parts, const struct image_partition_entry *parts, size_t *len) {
 	*len = 0x1814;
 
 	size_t i;
@@ -374,14 +444,17 @@ static void * generate_factory_image(const char *vendor, const struct image_part
 	if (!image)
 		error(1, errno, "malloc");
 
+	memset(image, 0xff, *len);
+
 	put32(image, *len);
 
-	size_t vendor_len = strlen(vendor);
-	put32(image+0x14, vendor_len);
-	memcpy(image+0x18, vendor, vendor_len);
-	memset(image+0x18+vendor_len, 0xff, 4092-vendor_len);
+	if (vendor) {
+		size_t vendor_len = strlen(vendor);
+		put32(image+0x14, vendor_len);
+		memcpy(image+0x18, vendor, vendor_len);
+	}
 
-	put_partitions(image + 0x1014, parts);
+	put_partitions(image + 0x1014, flash_parts, parts);
 	put_md5(image+0x04, image+0x14, *len-0x14);
 
 	return image;
@@ -389,38 +462,38 @@ static void * generate_factory_image(const char *vendor, const struct image_part
 
 /**
    Generates the firmware image in sysupgrade format
-
-   This makes some assumptions about the provided flash and image partition tables and
-   should be generalized when TP-LINK starts building its safeloader into hardware with
-   different flash layouts.
 */
-static void * generate_sysupgrade_image(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) {
-	const struct flash_partition_entry *flash_os_image = &flash_parts[5];
-	const struct flash_partition_entry *flash_soft_version = &flash_parts[6];
-	const struct flash_partition_entry *flash_support_list = &flash_parts[7];
-	const struct flash_partition_entry *flash_file_system = &flash_parts[8];
-
-	const struct image_partition_entry *image_os_image = &image_parts[3];
-	const struct image_partition_entry *image_soft_version = &image_parts[1];
-	const struct image_partition_entry *image_support_list = &image_parts[2];
-	const struct image_partition_entry *image_file_system = &image_parts[4];
-
-	assert(strcmp(flash_os_image->name, "os-image") == 0);
-	assert(strcmp(flash_soft_version->name, "soft-version") == 0);
-	assert(strcmp(flash_support_list->name, "support-list") == 0);
-	assert(strcmp(flash_file_system->name, "file-system") == 0);
-
-	assert(strcmp(image_os_image->name, "os-image") == 0);
-	assert(strcmp(image_soft_version->name, "soft-version") == 0);
-	assert(strcmp(image_support_list->name, "support-list") == 0);
-	assert(strcmp(image_file_system->name, "file-system") == 0);
-
-	if (image_os_image->size > flash_os_image->size)
-		error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
-	if (image_file_system->size > flash_file_system->size)
-		error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
-
-	*len = flash_file_system->base - flash_os_image->base + image_file_system->size;
+static void * generate_sysupgrade_image(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len, const char *first_partition_name, const char *last_partition_name) {
+	size_t i, j;
+	size_t flash_first_partition_index = 0;
+	size_t flash_last_partition_index = 0;
+	const struct flash_partition_entry *flash_first_partition = NULL;
+	const struct flash_partition_entry *flash_last_partition = NULL;
+	const struct image_partition_entry *image_last_partition = NULL;
+
+	/** Find fisrt and last partitions */
+	for (i = 0; flash_parts[i].name; i++) {
+		if (!strcmp(flash_parts[i].name, first_partition_name)) {
+			flash_first_partition = &flash_parts[i];
+			flash_first_partition_index = i;
+		} else if (!strcmp(flash_parts[i].name, last_partition_name)) {
+			flash_last_partition = &flash_parts[i];
+			flash_last_partition_index = i;
+		}
+	}
+	assert(flash_first_partition && flash_last_partition);
+	assert(flash_first_partition_index < flash_last_partition_index);
+
+	/** Find last partition from image to calculate needed size */
+	for (i = 0; image_parts[i].name; i++) {
+		if (!strcmp(image_parts[i].name, last_partition_name)) {
+			image_last_partition = &image_parts[i];
+			break;
+		}
+	}
+	assert(image_last_partition);
+
+	*len = flash_last_partition->base - flash_first_partition->base + image_last_partition->size;
 
 	uint8_t *image = malloc(*len);
 	if (!image)
@@ -428,31 +501,39 @@ static void * generate_sysupgrade_image(const struct flash_partition_entry *flas
 
 	memset(image, 0xff, *len);
 
-	memcpy(image, image_os_image->data, image_os_image->size);
-	memcpy(image + flash_soft_version->base - flash_os_image->base, image_soft_version->data, image_soft_version->size);
-	memcpy(image + flash_support_list->base - flash_os_image->base, image_support_list->data, image_support_list->size);
-	memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
+	for (i = flash_first_partition_index; i <= flash_last_partition_index; i++) {
+		for (j = 0; image_parts[j].name; j++) {
+			if (!strcmp(flash_parts[i].name, image_parts[j].name)) {
+				if (image_parts[j].size > flash_parts[i].size)
+					error(1, 0, "%s partition too big (more than %u bytes)", flash_parts[i].name, (unsigned)flash_parts[i].size);
+				memcpy(image + flash_parts[i].base - flash_first_partition->base, image_parts[j].data, image_parts[j].size);
+				break;
+			}
+			/** Make sure that partition found */
+			assert(image_parts[j].name);
+		}
+	}
 
 	return image;
 }
 
 
-/** Generates an image for CPE210/220/510/520 and writes it to a file */
-static void do_cpe510(const char *output, const char *kernel_image, const char *rootfs_image, uint32_t rev, bool add_jffs2_eof, bool sysupgrade) {
+/** Generates an image and writes it to a file */
+static void build_fw(const char *output, const char *kernel_image, const char *rootfs_image, uint32_t rev, bool add_jffs2_eof, bool sysupgrade, const struct firmware_info *info) {
 	struct image_partition_entry parts[6] = {};
 
-	parts[0] = make_partition_table(cpe510_partitions);
+	parts[0] = make_partition_table(info->flash_partitions);
 	parts[1] = make_soft_version(rev);
-	parts[2] = make_support_list(cpe510_support_list);
+	parts[2] = make_support_list(info->support_list);
 	parts[3] = read_file("os-image", kernel_image, false);
 	parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
 
 	size_t len;
 	void *image;
 	if (sysupgrade)
-		image = generate_sysupgrade_image(cpe510_partitions, parts, &len);
+		image = generate_sysupgrade_image(info->flash_partitions, parts, &len, info->first_sysupgrade_partition, info->last_sysupgrade_partition);
 	else
-		image = generate_factory_image(cpe510_vendor, parts, &len);
+		image = generate_factory_image(info->vendor, info->flash_partitions, parts, &len);
 
 	FILE *file = fopen(output, "wb");
 	if (!file)
@@ -490,10 +571,28 @@ static void usage(const char *argv0) {
 };
 
 
+static const struct firmware_info *find_board(const char *id)
+{
+	struct firmware_info *ret;
+	struct firmware_info *board;
+
+	ret = NULL;
+	for (board = boards; board->id != NULL; board++){
+		if (strcasecmp(id, board->id) == 0) {
+			ret = board;
+			break;
+		}
+	};
+
+	return ret;
+}
+
+
 int main(int argc, char *argv[]) {
 	const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
 	bool add_jffs2_eof = false, sysupgrade = false;
 	unsigned rev = 0;
+	const struct firmware_info *info;
 
 	while (true) {
 		int c;
@@ -550,10 +649,11 @@ int main(int argc, char *argv[]) {
 	if (!output)
 		error(1, 0, "no output filename has been specified");
 
-	if (strcmp(board, "CPE510") == 0)
-		do_cpe510(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade);
-	else
+	info = find_board(board);
+	if (info == NULL)
 		error(1, 0, "unsupported board %s", board);
 
+	build_fw(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info);
+
 	return 0;
 }
-- 
2.6.4
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list