mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2024-12-18 04:44:42 +00:00
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:
parent
588933ae9a
commit
1a211af2cb
@ -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
|
||||
|
@ -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;
|
||||
struct soft_version s = {
|
||||
.pad1 = 0xff,
|
||||
|
||||
s->version_major = 0;
|
||||
s->version_minor = 0;
|
||||
s->version_patch = 0;
|
||||
.version_major = 0,
|
||||
.version_minor = 0,
|
||||
.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);
|
||||
.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),
|
||||
|
||||
if (info->soft_ver_compat_level > 0)
|
||||
*(uint32_t *)(entry.data + sizeof(struct soft_version)) =
|
||||
htonl(info->soft_ver_compat_level);
|
||||
.compat_level = htonl(info->soft_ver_compat_level)
|
||||
};
|
||||
|
||||
entry.data[entry.size-1] = 0xff;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user