From patchwork Tue Jun 8 09:45:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Petr_=C5=A0tetiar?= X-Patchwork-Id: 1489256 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=2607:7c80:54:e::133; helo=bombadil.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=bombadil.20210309 header.b=GsR4avKI; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (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 4FzllY2QGKz9sSn for ; Tue, 8 Jun 2021 19:47:37 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; 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=QeBPap0u47/4kb4OjbLyYK09kXH5h4V26MhFupNUlUM=; b=GsR4avKIKUSlLw YQI30XN0dlWk9v5SgM6ZroTk6CjASroJTiUFnngcfuBPedk78lTU59mXgtH3n4UDymll1FQPdsTZL yILPaoK/MG4jx3jV6G+x6XkgLNeKp0MTQTCltNOO3Vd+ejPc24ZXqydmsBSOYIiqrqkFgpEGAbI14 iFPDpbsMm/SRa9IjHjDnw6xocrNbnQi0W+MUYL/JkUqCKYcPMlmbhiIHWZ19YZUIDwZE+/GRSzo63 2z1/sGxhb6ApbtdcRYip8pIp55C+6ROEqlw68LVsRngXD3uRxx/ppYMS/FGEQI0sUu0YBiPPsYsll 8uLz6mpFgwpJYqOJwkcg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1lqYIp-007UpQ-OS; Tue, 08 Jun 2021 09:45:43 +0000 Received: from smtp-out.xnet.cz ([178.217.244.18]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1lqYIU-007Uht-P4 for openwrt-devel@lists.openwrt.org; Tue, 08 Jun 2021 09:45:26 +0000 Received: from meh.true.cz (meh.true.cz [108.61.167.218]) (Authenticated sender: petr@true.cz) by smtp-out.xnet.cz (Postfix) with ESMTPSA id 587AA18940; Tue, 8 Jun 2021 11:45:17 +0200 (CEST) Received: by meh.true.cz (OpenSMTPD) with ESMTP id adae1c7e; Tue, 8 Jun 2021 11:44:51 +0200 (CEST) From: =?utf-8?q?Petr_=C5=A0tetiar?= To: openwrt-devel@lists.openwrt.org Cc: =?utf-8?q?Bj=C3=B8rn_Mork?= Subject: [PATCH 21.02 01/11] firmware-utils: zytrx: Add util for ZyXEL specific header Date: Tue, 8 Jun 2021 11:45:02 +0200 Message-Id: <20210608094512.29956-2-ynezz@true.cz> In-Reply-To: <20210608094512.29956-1-ynezz@true.cz> References: <20210608094512.29956-1-ynezz@true.cz> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210608_024523_055712_F21CD783 X-CRM114-Status: GOOD ( 27.44 ) X-Spam-Score: 0.0 (/) X-Spam-Report: =?unknown-8bit?q?Spam_detection_software=2C_running_on_the_sy?= =?unknown-8bit?q?stem_=22bombadil=2Einfradead=2Eorg=22=2C?= =?unknown-8bit?q?_has_NOT_identified_this_incoming_email_as_spam=2E__The_ori?= =?unknown-8bit?q?ginal?= =?unknown-8bit?q?_message_has_been_attached_to_this_so_you_can_view_it_or_la?= =?unknown-8bit?q?bel?= =?unknown-8bit?q?_similar_future_email=2E__If_you_have_any_questions=2C_see?= =?unknown-8bit?q?_the_administrator_of_that_system_for_details=2E?= =?unknown-8bit?q?_?= =?unknown-8bit?q?_Content_preview=3A__From=3A_Bj=C3=B8rn_Mork_=3Cbjorn=40mor?= =?unknown-8bit?q?k=2Eno=3E_The_ZyXEL_NR7101_prepend?= =?unknown-8bit?q?_an_additional_header_to_U-Boot_images=2E_This_header_use_t?= =?unknown-8bit?q?he_TRX_magic_0x30524448?= =?unknown-8bit?q?_=28HDR0=29=2C_but_is_incompatible_with_TRX_images=2E_This_?= =?unknown-8bit?q?code_is_reverse-engineered?= =?unknown-8bit?q?_based_on_matching_32_bit_numbers_found_in_the_header_with_?= =?unknown-8bit?q?lengths_and_different?= =?unknown-8bit?q?_checksum_calculations_of_the_vendor_images_found_on_the_de?= =?unknown-8bit?q?vice=2E_The_result?= =?unknown-8bit?b?IHdhcyBtYXRjaGVkIGEgWy4uLl0g?= =?unknown-8bit?q?_?= =?unknown-8bit?q?_Content_analysis_details=3A___=280=2E0_points=2C_5=2E0_req?= =?unknown-8bit?q?uired=29?= =?unknown-8bit?q?_?= =?unknown-8bit?q?_pts_rule_name______________description?= =?unknown-8bit?q?_----_----------------------_------------------------------?= =?unknown-8bit?q?--------------------?= =?unknown-8bit?q?_0=2E0_SPF=5FNONE_______________SPF=3A_sender_does_not_publ?= =?unknown-8bit?q?ish_an_SPF_Record?= =?unknown-8bit?q?_0=2E0_SPF=5FHELO=5FNONE__________SPF=3A_HELO_does_not_publ?= =?unknown-8bit?q?ish_an_SPF_Record?= 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 From: Bjørn Mork 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 (cherry picked from commit 48cad07a55246317d66fee4ced25cb62a2bae3d3) Tested-by: Bjørn Mork # ZyXEL NR7101 --- 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 28f8f0555a55..cec8398a27d5 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -97,6 +97,7 @@ define Host/Compile $(call cc,wrt400n cyg_crc32) $(call cc,xorimage) $(call cc,zyimage,-Wall) + $(call cc,zytrx,-Wall) $(call cc,zyxbcm) 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; +}