From patchwork Mon Apr 19 11:00:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Bj=C3=B8rn_Mork?= X-Patchwork-Id: 1467911 X-Patchwork-Delegate: ynezz@true.cz Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.openwrt.org (client-ip=2001:8b0:10b:1:d65d:64ff:fe57:4e05; helo=desiato.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=desiato.20200630 header.b=bdyOjWF8; dkim=fail reason="signature verification failed" (1024-bit key; secure) header.d=mork.no header.i=@mork.no header.a=rsa-sha256 header.s=b header.b=KtHBOTOf; dkim-atps=neutral Received: from desiato.infradead.org (desiato.infradead.org [IPv6:2001:8b0:10b:1:d65d:64ff:fe57:4e05]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4FP3nv62pmz9vGp for ; Mon, 19 Apr 2021 21:03:15 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=cwGMp661tlDYIPA0X9Mu1Pt14ij/hTAAOx12ygQwSoo=; b=bdyOjWF8Yvq2kNl92mxUAeGtC VTcsEz86Sxau46bi2pbLiR0EgTXN/Bd3OnwLGX4fBeRVGf+at8skQRatv4eR4Zrfqt6KmWe4jfgrj UX7XCXvEZCrnYb1ORsiCQ1343oWsGsEsPhS9kvKgZBeWpFy5Zrs4bHu0Vr4M0Io9j4N8CjOE5637O rIp7eZiWW/oX+AYefzpX67ktZRWZXZ4fZFJgz6+pdVGK/Dv77ROo8hY3d7H7/7n01IxwQakYz9F6R en57dufbY20yW3eDpITfeYuYto9rzDf/B0irKZC9fZgcIQjFZ8I8kHD3RprmIUEonCxsSOBTaPM/R YK7wIlUsg==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lYReV-009j6J-4M; Mon, 19 Apr 2021 11:01:15 +0000 Received: from canardo.mork.no ([2001:4641::1]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lYReJ-009j3h-He for openwrt-devel@lists.openwrt.org; Mon, 19 Apr 2021 11:01:06 +0000 Received: from canardo.mork.no (ip6-localhost [IPv6:0:0:0:0:0:0:0:1]) by canardo.mork.no (8.15.2/8.15.2) with ESMTPS id 13JB0xYE006409 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=OK); Mon, 19 Apr 2021 13:00:59 +0200 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mork.no; s=b; t=1618830059; bh=3f3pArF0snI+Z0Nx5p/kIZYKNsILj80pDQZRfkGmuj0=; h=From:To:Cc:Subject:Date:Message-Id:References:From; b=KtHBOTOfm/EBo1Q3fAuRFhK4fEh7XR5WkRwIpykH2aSMKNfwEKnMimYJedTal5N14 HFezW90WOBHggz8y2vLE5NqBNoZi62HwI+e4iGImftFimzNWU1EjwqD736FPzlQEzY hEMbhcDf99yZ9bNIrxPSfcFiPpPE4WerEHOeCvFs= Received: (from bjorn@localhost) by canardo.mork.no (8.15.2/8.15.2/Submit) id 13JB0xZS006402; Mon, 19 Apr 2021 13:00:59 +0200 From: =?utf-8?q?Bj=C3=B8rn_Mork?= To: openwrt-devel@lists.openwrt.org Cc: =?utf-8?q?Bj=C3=B8rn_Mork?= Subject: [PATCH 1/2] firmware-utils: zytrx: Add util for ZyXEL specific header Date: Mon, 19 Apr 2021 13:00:55 +0200 Message-Id: <20210419110056.6348-2-bjorn@mork.no> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210419110056.6348-1-bjorn@mork.no> References: <20210419110056.6348-1-bjorn@mork.no> MIME-Version: 1.0 X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT shortcircuit=ham autolearn=disabled version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on canardo.mork.no X-Virus-Scanned: clamav-milter 0.102.4 at canardo X-Virus-Status: Clean X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210419_120103_821304_4FF19518 X-CRM114-Status: GOOD ( 28.82 ) X-Spam-Score: -2.5 (--) X-Spam-Report: Spam detection software, running on the system "desiato.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: The ZyXEL NR7101 prepend an additional header to U-Boot images. This header use the TRX magic 0x30524448 (HDR0), but is incompatible with TRX images. This code is reverse-engineered based on matching 32 bit numbers found in the header with lengths and different checksum calculations of the vendor images found on the device. The result was matched a [...] Content analysis details: (-2.5 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [2001:4641:0:0:0:0:0:1 listed in] [list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org The ZyXEL NR7101 prepend an additional header to U-Boot images. This header use the TRX magic 0x30524448 (HDR0), but is incompatible with TRX images. This code is reverse-engineered based on matching 32 bit numbers found in the header with lengths and different checksum calculations of the vendor images found on the device. The result was matched against the validation output produced by the bootloader to name the associated header fields. Example bootloader validation output: Zyxel TRX Image 1 --> Found! Header Checksum OK ============ZyXEL header information================== chipId : MT7621A boardId : NR7101 modelId : 07 01 00 01 kernel_len : (14177560) kernelChksum : (0x8DD31F69) swVersionInt : 1.00(ABUV.0)D1 swVersionExt : 1.00(ABUV.0)D1 Zyxel TRX Image 2 --> Found! Header Checksum OK ============ZyXEL header information================== chipId : MT7621A boardId : NR7101 modelId : 07 01 00 01 kernel_len : (14176660) kernelChksum : (0x951A7637) swVersionInt : 1.00(ABUV.0)D0 swVersionExt : 1.00(ABUV.0)D0 ================================================= Check image validation: Image1 Header Magic Number --> OK Image2 Header Magic Number --> OK Image1 Header Checksum --> OK Image2 Header Checksum --> OK Image1 Data Checksum --> OK Image2 Data Checksum --> OK Image1 Stable Flag --> Stable Image1 Try Counter --> 0 Image1: OK Image2: OK The coverage and algorithm for the kernelChksum field is unknown. This field is not validated by the bootloader or the OEM firmware upgrade tool. It is therefore set to a static value for now. The swVersion fields contain free form string values. The OEM firmware use ZyXEL structured version numbers as shown above. The strings are not interpreted or validated on boot, so they can be repurposed for anything we want the bootloader to display to the user. But the OEM web GUI fails to flash images with freeform strings. The purpose of the other strings in the header is not known. The values appear to be static. We assume they are fixed for now, until we have other examples. One of these strings is the platform name, which is taken as an input parameter for support other members of the device family. Signed-off-by: Bjørn Mork --- tools/firmware-utils/Makefile | 1 + tools/firmware-utils/src/zytrx.c | 226 +++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 tools/firmware-utils/src/zytrx.c diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 285722c3c29f..72cd513a45d4 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -97,6 +97,7 @@ define Host/Compile $(call cc,wrt400n cyg_crc32,-Wall) $(call cc,xorimage,-Wall) $(call cc,zyimage,-Wall) + $(call cc,zytrx,-Wall) $(call cc,zyxbcm,-Wall) endef diff --git a/tools/firmware-utils/src/zytrx.c b/tools/firmware-utils/src/zytrx.c new file mode 100644 index 000000000000..302efc601065 --- /dev/null +++ b/tools/firmware-utils/src/zytrx.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * zytrx - add header to images for ZyXEL NR7101 + * + * Based on add_header.c - partially based on OpenWrt's + * motorola-bin.c + * + * Copyright (C) 2008 Imre Kaloz + * Gabor Juhos + * Copyright (C) 2021 Bjørn Mork + + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BPB 8 /* bits/byte */ + +static uint32_t crc32[1<> 1)) : (crc >> 1); + crc32[n] = crc; + } +} + +static uint32_t crc32buf(const unsigned char *buf, size_t len) +{ + uint32_t crc = 0xFFFFFFFF; + + for (; len; len--, buf++) + crc = crc32[(uint8_t)crc ^ *buf] ^ (crc >> BPB); + return ~crc; +} + +/* HDR0 reversed, to be stored as BE */ +#define MAGIC 0x30524448 /* HDR0 reversed, to be stored as BE */ + +/* All numbers are stored as BE */ +struct zytrx_t { + uint32_t magic; + uint32_t len_h; /* Length of this header */ + uint32_t len_t; /* Total length of file */ + uint32_t crc32_p; /* Bit inverted 32-bit CRC of image payload */ + uint8_t verInt[32]; /* String "5.0.0.0\n" zero padded */ + uint8_t verExt[32]; /* String "\n" zero padded */ + uint32_t len_p; /* Length of image payload */ + uint8_t pad1[12]; /* zero padding(?) */ + uint8_t code[164]; /* string "3 6035 122 0\n" zero padded */ + uint8_t chipid[8]; /* string "MT7621A" zero padded */ + uint8_t boardid[16]; /* string "NR7101" zero padded */ + uint32_t modelid; /* modelid as 4 BCD digits: 0x07010001 */ + uint8_t pad2[8]; /* zero padding(?) */ + uint8_t swVersionInt[32]; /* ZyXEL version string: "1.00(ABUV.0)D0" zero padded */ + uint8_t swVersionExt[32]; /* identical to swVersionInt */ + uint8_t pad4[4]; /* zero padding(?) */ + uint32_t kernelChksum; /* no idea how this is computed - reported but not validated */ + uint8_t pad5[4]; /* zero padding(?) */ + uint32_t crc32_h; /* Bit inverted 32-bit CRC of this header payload */ + uint8_t pad6[4]; /* zero padding(?) */ +}; + +/* static?() field values of unknown meaning - maybe ove to board + * table when we know the significance + */ +#define VER_INT "5.0.0.0\n" +#define VER_EXT "\n" +#define CODE "3 6035 122 0\n" +#define KERNELCHKSUM 0x12345678 + +/* table of supported devices using this header format */ +static struct board_t { + uint8_t chipid[8]; + uint8_t boardid[16]; + uint32_t modelid; +} boards[] = { + { "MT7621A", "NR7101", 0x07010001 }, + {} +}; + +static int find_board(struct zytrx_t *h, char *board) +{ + struct board_t *p; + + for (p = boards; p->modelid; p++) { + if (strncmp((const char *)p->boardid, board, sizeof(p->boardid))) + continue; + memcpy(h->chipid, p->chipid, sizeof(h->chipid)); + memcpy(h->boardid, p->boardid, sizeof(h->boardid)); + h->modelid = htonl(p->modelid); + return 0; + } + return -1; +} + +static void usage(const char *name) +{ + struct board_t *p; + + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s -B -v -i [-o ]\n\n", name); + fprintf(stderr, "Supported values:\n"); + for (p = boards; p->modelid; p++) + fprintf(stderr, "\t%-12s\n", p->boardid); + fprintf(stderr, "\nExample:\n"); + fprintf(stderr, " %s -B %s -v foobar-1.0 -i my.img -o out.img\n\n", name, + boards[0].boardid); + exit(EXIT_FAILURE); +} + +static void errexit(const char *msg) +{ + fprintf(stderr, "ERR: %s: %s\n", msg, errno ? strerror(errno) : "unknown"); + exit(EXIT_FAILURE); +} + +static void *map_input(const char *name, size_t *len) +{ + struct stat stat; + void *mapped; + int fd; + + fd = open(name, O_RDONLY); + if (fd < 0) + return NULL; + if (fstat(fd, &stat) < 0) { + close(fd); + return NULL; + } + *len = stat.st_size; + mapped = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (close(fd) < 0) + return NULL; + return mapped; +} + +int main(int argc, char **argv) +{ + int c, fdout = STDOUT_FILENO; + void *input_file = NULL; + size_t file_len, len; + uint32_t crc; + struct zytrx_t h = { + .magic = htonl(MAGIC), + .len_h = htonl(sizeof(h)), + .verInt = VER_INT, + .verExt = VER_EXT, + .code = CODE, + .kernelChksum = htonl(KERNELCHKSUM), + }; + + while ((c = getopt(argc, argv, "B:v:i:o:")) != -1) { + switch (c) { + case 'B': + if (find_board(&h, optarg) < 0) + errexit("unsupported board"); + break; + case 'v': + len = strlen(optarg); + if (len > sizeof(h.swVersionInt)) + errexit("version string too long"); + memcpy(h.swVersionInt, optarg, len); + memcpy(h.swVersionExt, optarg, len); + break; + case 'i': + input_file = map_input(optarg, &file_len); + if (!input_file) + errexit(optarg); + break; + case 'o': + fdout = open(optarg, O_WRONLY | O_CREAT, 0644); + if (fdout < 0) + errexit(optarg); + break; + default: + usage(argv[0]); + } + } + + /* required paremeters */ + if (!input_file || !h.modelid || !h.swVersionInt[0]) + usage(argv[0]); + + /* length fields */ + h.len_t = htonl(sizeof(h) + file_len); + h.len_p = htonl(file_len); + + /* crc fields */ + init_crc32(); + crc = crc32buf(input_file, file_len); + h.crc32_p = htonl(~crc); + crc = crc32buf((unsigned char *)&h, sizeof(h)); + h.crc32_h = htonl(~crc); + + /* dump new image */ + write(fdout, &h, sizeof(h)); + write(fdout, input_file, file_len); + + /* close files */ + munmap(input_file, file_len); + if (fdout != STDOUT_FILENO) + close(fdout); + + return EXIT_SUCCESS; +}