From 6411eac5da19359c45fdd9543725369ba9526384 Mon Sep 17 00:00:00 2001 From: Steve Glennon Date: Tue, 9 Apr 2019 14:46:32 -0600 Subject: [PATCH] ipq40xx: add factory image for EnGenius ENS620EXT Extended mksenaofw to support new "capwap" header structure. This supports flashing from factory 3.0.0, 3.0.1, 3.1.0 and 3.5.5 firmware. Note that the factory image format changes for 3.1 and later firmware, and that the 3.1.0 and 3.5.5 Engenius firmware will refuse the factory_30.bin file. Similarly, the 3.0.0 and 3.0.1 Engenius firmware will refuse the factory_35.bin file. Flashing from the Engenius 3.1.0 firmware with the factory_35.bin firmware has not been tested, as 3.1.0 firmware (Engenius "middleFW") is only intended as part of the upgrade path to 3.5.5 firmware. Modified ipq40xx image Makefile to appropriately invoke mksenaofw with new parameters to configure the capwap header. Note that there is currently no method to return to factory firmware, so this is a one-way street. Path from factory 3.0.0 and 3.0.1 (EnGenius) software to OpenWrt is to navigate to 192.168.1.1 on the stock firmware and navigate to the firmware menu. Then copy the URL you have for that page, something like http://192.168.1.1/cgi-bin/luci/;stok=12345abcdef/admin/system/flashops and replace the trailing /admin/system/flashops with just /easyflashops You should then be presented with a simple "Firmware Upgrade" page. On that page, BE SURE TO CLEAR the "Keep Settings:" checkbox. Choose the openwrt-ipq40xx-engenius_ens620ext-squashfs-factory_30.bin, click "Upgrade" and on the following page select "Proceed". Path from factory 3.5.5 (EnGenius) software to OpenWrt is simply to use the stock firmware update menu. Choose the openwrt-ipq40xx-engenius_ens620ext-squashfs-factory_35.bin and click "Upload" and "Proceed". The device should then flash the OpenWrt firmware and reboot. Note that this resets the device to a default configuration with Wi-Fi disabled, LAN1/PoE acting as a WAN port (running DHCP client) and LAN2 acting as a LAN port with a DHCP server on 192.168.1.x (AP is at 192.168.1.1) Signed-off-by: Steve Glennon Signed-off-by: Christian Lamparter [sorry, for unfixing the 80-lines eyesores.] --- target/linux/ipq40xx/image/Makefile | 40 ++++-- tools/firmware-utils/Makefile | 2 +- tools/firmware-utils/src/mksenaofw.c | 204 +++++++++++++++++++++++---- 3 files changed, 207 insertions(+), 39 deletions(-) diff --git a/target/linux/ipq40xx/image/Makefile b/target/linux/ipq40xx/image/Makefile index 626223a7cb01..ae146cdc5486 100644 --- a/target/linux/ipq40xx/image/Makefile +++ b/target/linux/ipq40xx/image/Makefile @@ -51,6 +51,14 @@ define Device/DniImage endef DEVICE_VARS += NETGEAR_BOARD_ID NETGEAR_HW_ID +define Build/SenaoFW + -$(STAGING_DIR_HOST)/bin/mksenaofw \ + -n $(BOARD_NAME) -r $(VENDOR_ID) -p $(1) \ + -c $(DATECODE) -w $(2) -x $(CW_VER) -t 0 \ + -e $@ \ + -o $@.new + @cp $@.new $@ +endef define Device/8dev_jalapeno $(call Device/FitImage) @@ -168,17 +176,27 @@ endef TARGET_DEVICES += engenius_eap1300 define Device/engenius_ens620ext - $(call Device/FitImage) - DEVICE_DTS := qcom-ipq4018-ens620ext - DEVICE_DTS_CONFIG := config@4 - BLOCKSIZE := 64k - PAGESIZE := 256 - DEVICE_TITLE := EnGenius ENS620EXT - IMAGE_SIZE := 21823488 - KERNEL_SIZE := 5120k - FILESYSTEMS := squashfs - IMAGES := sysupgrade.bin - IMAGE/sysupgrade.bin := append-kernel | append-rootfs | pad-rootfs | check-size $$$$(IMAGE_SIZE) | append-metadata + $(call Device/FitImage) + DEVICE_DTS := qcom-ipq4018-ens620ext + DEVICE_DTS_CONFIG := config@4 + BLOCKSIZE := 64k + PAGESIZE := 256 + DEVICE_TITLE := EnGenius ENS620EXT + BOARD_NAME := ENS620EXT + VENDOR_ID := 0x0101 + PRODUCT_ID := 0x79 + PRODUCT_ID_NEW := 0xA4 + DATECODE := 190507 + FW_VER := 3.1.2 + FW_VER_NEW := 3.5.6 + CW_VER := 1.8.99 + IMAGE_SIZE := 21823488 + KERNEL_SIZE := 5120k + FILESYSTEMS := squashfs + IMAGES := sysupgrade.bin factory_30.bin factory_35.bin + IMAGE/sysupgrade.bin := append-kernel | append-rootfs | pad-rootfs | check-size $$$$(IMAGE_SIZE) | append-metadata + IMAGE/factory_30.bin := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-rootfs | pad-rootfs | check-size $$$$(IMAGE_SIZE) | SenaoFW $$$$(PRODUCT_ID) $$$$(FW_VER) + IMAGE/factory_35.bin := qsdk-ipq-factory-nor | check-size $$$$(IMAGE_SIZE) | SenaoFW $$$$(PRODUCT_ID_NEW) $$$$(FW_VER_NEW) DEVICE_PACKAGES := ipq-wifi-engenius_ens620ext endef TARGET_DEVICES += engenius_ens620ext diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 76d5929af529..bde90f0ecd15 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -23,7 +23,7 @@ define Host/Compile $(call cc,otrx) $(call cc,motorola-bin) $(call cc,dgfirmware) - $(call cc,mksenaofw md5) + $(call cc,mksenaofw md5, -Wall --std=gnu99) $(call cc,trx2usr) $(call cc,ptgen) $(call cc,srec2bin) diff --git a/tools/firmware-utils/src/mksenaofw.c b/tools/firmware-utils/src/mksenaofw.c index 0f10ebdfbeba..b0fb7203c330 100644 --- a/tools/firmware-utils/src/mksenaofw.c +++ b/tools/firmware-utils/src/mksenaofw.c @@ -55,6 +55,7 @@ typedef enum { } op_mode; static firmware_type FIRMWARE_TYPES[] = { + { 0x00, "combo" }, /* Used for new capwap-included style header */ { 0x01, "bootloader" }, { 0x02, "kernel" }, { 0x03, "kernelapp" }, @@ -70,6 +71,21 @@ static firmware_type FIRMWARE_TYPES[] = { { 0x0c, "langpack (D-Link)" } }; +#define MOD_DEFAULT 0x616C6C00 +#define SKU_DEFAULT 0x0 +#define DATECODE_NONE 0xFFFFFFFF +#define FIRMWARE_TYPE_NONE 0xFF + +struct capwap_header { + uint32_t mod; + uint32_t sku; + uint32_t firmware_ver[3]; + uint32_t datecode; + uint32_t capwap_ver[3]; + uint32_t model_size; + uint8_t model[]; +}; + static long get_file_size(const char *filename) { FILE *fp_file; @@ -84,15 +100,14 @@ static long get_file_size(const char *filename) return result; } -static int header_checksum(void *data, int len) +static int header_checksum(void *data, size_t len) { - int i; - int sum; + int sum = 0; /* shouldn't this be unsigned ? */ + size_t i; - sum = 0; - if (data != NULL && len >= 0) { + if (data != NULL && len > 0) { for (i = 0; i < len; ++i) - sum += *(unsigned char *) (data + i); + sum += ((unsigned char *)data)[i]; return sum; } @@ -124,18 +139,20 @@ static int md5_file(const char *filename, uint8_t *dst) } static int encode_image(const char *input_file_name, - const char *output_file_name, img_header *header, int block_size) + const char *output_file_name, img_header *header, + struct capwap_header *cw_header, int block_size) { char buf[BUF_SIZE]; - size_t bytes_read; size_t pad_len = 0; size_t bytes_avail; + size_t bytes_read; - FILE *fp_input; FILE *fp_output; + FILE *fp_input; - int i; + int model_size; long magic; + size_t i; fp_input = fopen(input_file_name, "r+b"); if (!fp_input) { @@ -172,6 +189,11 @@ static int encode_image(const char *input_file_name, } header->zero = 0; header->chksum = header_checksum(header, HDR_LEN); + if (cw_header) { + header->chksum += header_checksum(cw_header, + sizeof(struct capwap_header) + cw_header->model_size); + } + header->head = htonl(header->head); header->vendor_id = htonl(header->vendor_id); header->product_id = htonl(header->product_id); @@ -183,6 +205,22 @@ static int encode_image(const char *input_file_name, fwrite(header, HDR_LEN, 1, fp_output); + if (cw_header) { + model_size = cw_header->model_size; + cw_header->mod = htonl(cw_header->mod); + cw_header->sku = htonl(cw_header->sku); + cw_header->firmware_ver[0] = htonl(cw_header->firmware_ver[0]); + cw_header->firmware_ver[1] = htonl(cw_header->firmware_ver[1]); + cw_header->firmware_ver[2] = htonl(cw_header->firmware_ver[2]); + cw_header->datecode = htonl(cw_header->datecode); + cw_header->capwap_ver[0] = htonl(cw_header->capwap_ver[0]); + cw_header->capwap_ver[1] = htonl(cw_header->capwap_ver[1]); + cw_header->capwap_ver[2] = htonl(cw_header->capwap_ver[2]); + cw_header->model_size = htonl(cw_header->model_size); + fwrite(cw_header, sizeof(struct capwap_header) + model_size, 1, + fp_output); + } + while (!feof(fp_input) || pad_len > 0) { if (!feof(fp_input)) @@ -212,32 +250,33 @@ static int encode_image(const char *input_file_name, int decode_image(const char *input_file_name, const char *output_file_name) { - img_header header; + struct capwap_header cw_header; char buf[BUF_SIZE]; + img_header header; + char *pmodel = NULL; FILE *fp_input; FILE *fp_output; - unsigned int i; size_t bytes_read; size_t bytes_written; + unsigned int i; fp_input = fopen(input_file_name, "r+b"); if (!fp_input) { fprintf(stderr, "Cannot open %s !!\n", input_file_name); - fclose(fp_input); return -1; } fp_output = fopen(output_file_name, "w+b"); if (!fp_output) { fprintf(stderr, "Cannot open %s !!\n", output_file_name); - fclose(fp_output); + fclose(fp_input); return -1; } if (fread(&header, 1, HDR_LEN, fp_input) != HDR_LEN) { - fprintf(stderr, "Incorrect header size!!"); + fprintf(stderr, "Incorrect header size reading base header!!"); fclose(fp_input); fclose(fp_output); return -1; @@ -251,6 +290,44 @@ int decode_image(const char *input_file_name, const char *output_file_name) header.chksum = ntohl(header.chksum); header.magic = ntohl(header.magic); + /* read capwap header if firmware_type is zero */ + if (header.firmware_type == 0) { + if (fread(&cw_header, 1, sizeof(struct capwap_header), + fp_input) != sizeof(struct capwap_header)) { + fprintf(stderr, "Incorrect header size reading capwap_header!!"); + fclose(fp_input); + fclose(fp_output); + return -1; + } + cw_header.mod = ntohl(cw_header.mod); + cw_header.sku = ntohl(cw_header.sku); + cw_header.firmware_ver[0] = ntohl(cw_header.firmware_ver[0]); + cw_header.firmware_ver[1] = ntohl(cw_header.firmware_ver[1]); + cw_header.firmware_ver[2] = ntohl(cw_header.firmware_ver[2]); + cw_header.datecode = ntohl(cw_header.datecode); + cw_header.capwap_ver[0] = ntohl(cw_header.capwap_ver[0]); + cw_header.capwap_ver[1] = ntohl(cw_header.capwap_ver[1]); + cw_header.capwap_ver[2] = ntohl(cw_header.capwap_ver[2]); + cw_header.model_size = ntohl(cw_header.model_size); + + pmodel = malloc(cw_header.model_size + 1); + if (pmodel) { + pmodel[cw_header.model_size] = '\0'; + if (fread(pmodel, 1, cw_header.model_size, fp_input) != + cw_header.model_size) { + fprintf(stderr, "Incorrect header size reading model name!!"); + fclose(fp_input); + fclose(fp_output); + return -1; + } + } else { + fprintf(stderr, "Incorrect header size reading model name!!"); + fclose(fp_input); + fclose(fp_output); + return -1; + } + } + bytes_written = 0; while (!feof(fp_input)) { @@ -281,7 +358,7 @@ int decode_image(const char *input_file_name, const char *output_file_name) static void usage(const char *progname, int status) { FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; - int i; + size_t i; fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); fprintf(stream, "\n" @@ -301,28 +378,47 @@ static void usage(const char *progname, int status) " -m set encoding magic \n" " -z enable image padding to \n" " -b set image , defaults to %u\n" + " -c add capwap header with (e.g. 171101)\n" + " -w firmware version for capwap header (e.g. 3.0.1)\n" + " -x capwap firmware version for capwap header (e.g. 1.8.53)\n" + " -n model name for capwap header (e.g. ENS620EXT)\n" " -h show this screen\n", DEFAULT_BLOCK_SIZE); exit(status); } int main(int argc, char *argv[]) { - int opt; - char *input_file, *output_file, *progname = NULL; + static const char period[2] = "."; + struct capwap_header cw_header; + img_header header; + + struct capwap_header *pcw_header = NULL; + char *output_file = NULL; + char *input_file = NULL; + char *progname = NULL; + char *mod_name = NULL; + char *token; + op_mode mode = NONE; - int tmp, i, pad = 0; + int tmp, pad = 0; int block_size; - img_header header; + size_t i; + int opt; block_size = DEFAULT_BLOCK_SIZE; progname = basename(argv[0]); - memset(&header, 0, sizeof( img_header )); + memset(&header, 0, sizeof(img_header)); header.magic = DEFAULT_MAGIC; header.head = DEFAULT_HEAD_VALUE; + header.firmware_type = FIRMWARE_TYPE_NONE; + memset(&cw_header, 0, sizeof(struct capwap_header)); + cw_header.mod = MOD_DEFAULT; + cw_header.sku = SKU_DEFAULT; + cw_header.datecode = DATECODE_NONE; strncpy( (char*)&header.version, DEFAULT_VERSION, VERSION_SIZE - 1); - while ((opt = getopt(argc, argv, ":o:e:d:t:v:r:p:m:b:h?z")) != -1) { + while ((opt = getopt(argc, argv, ":o:e:d:t:v:r:p:m:b:c:w:x:n:h?z")) != -1) { switch (opt) { case 'e': input_file = optarg; @@ -344,7 +440,7 @@ int main(int argc, char *argv[]) break; } } - if (header.firmware_type == 0) { + if (header.firmware_type == FIRMWARE_TYPE_NONE) { fprintf(stderr, "Invalid firmware type \"0\"!\n"); usage(progname, EXIT_FAILURE); } @@ -368,6 +464,31 @@ int main(int argc, char *argv[]) case 'b': block_size = strtol(optarg, 0, 10); break; + case 'c': + cw_header.datecode = strtoul(optarg, 0, 10); + break; + case 'w': + token = strtok(optarg, period); + i = 0; + while (token && (i < 3)) { + cw_header.firmware_ver[i++] = + strtoul(token, 0, 10); + token = strtok(NULL, period); + } + break; + case 'x': + token = strtok(optarg, period); + i = 0; + while (token && (i < 3)) { + cw_header.capwap_ver[i++] = + strtoul(token, 0, 10); + token = strtok(NULL, period); + } + break; + case 'n': + mod_name = optarg; + cw_header.model_size = strlen(mod_name); + break; case 'h': usage(progname, EXIT_SUCCESS); break; @@ -403,17 +524,46 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; } - if (header.firmware_type == 0) { - fprintf(stderr, "Firmware type must be defined\n"); + if ((header.firmware_type == 0) && + (cw_header.datecode == DATECODE_NONE)) { + fprintf(stderr, "Firmware type must be non-zero for non-capwap images\n"); usage(progname, EXIT_FAILURE); } if (header.vendor_id == 0 || header.product_id == 0) { - fprintf(stderr, "Vendor ID and Product ID must be defined and non-zero\n"); + fprintf(stderr, "Vendor ID and Product ID must be defined and non-zero\n"); usage(progname, EXIT_FAILURE); } - if (encode_image(input_file, output_file, &header, pad ? block_size : 0) < 0) + /* Check capwap header specific arguments */ + if (cw_header.datecode != DATECODE_NONE) { + if (!mod_name) { + fprintf(stderr, "Capwap header specified: model name must be specified\n"); + usage(progname, EXIT_FAILURE); + } + if (!cw_header.firmware_ver[0] && !cw_header.firmware_ver[1] && + !cw_header.firmware_ver[2]) { + fprintf(stderr, "Capwap header specified, fw_ver must be non-zero\n"); + } + if (!cw_header.capwap_ver[0] && !cw_header.capwap_ver[1] && + !cw_header.capwap_ver[2]) { + fprintf(stderr, "Capwap header specified, cw_ver must be non-zero\n"); + } + pcw_header = malloc(sizeof(struct capwap_header) + + cw_header.model_size); + if (pcw_header) { + memcpy(pcw_header, &cw_header, + sizeof(struct capwap_header)); + memcpy(&(pcw_header->model), mod_name, + cw_header.model_size); + } else { + fprintf(stderr, "Failed to allocate memory\n"); + return EXIT_FAILURE; + } + } + + if (encode_image(input_file, output_file, &header, pcw_header, + pad ? block_size : 0) < 0) return EXIT_FAILURE; return EXIT_SUCCESS; -- 2.30.2