From patchwork Wed Jul 27 19:31:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Hoffmann X-Patchwork-Id: 1661412 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.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=cRNayR+Q; dkim=fail reason="signature verification failed" (2048-bit key; secure) header.d=3e8.eu header.i=@3e8.eu header.a=rsa-sha256 header.s=mail20211217 header.b=Iw/MzgeE; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.openwrt.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LtPDR003dz9sGC for ; Thu, 28 Jul 2022 05:36:53 +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=3xPpnnghFQPAUS0vRKSY3MGPsPC2m7WRgl8XtR5mxMg=; b=cRNayR+Qm65L0e cg9QS+aNgNirQmktYgFL58yAuWe8TWwr/jK1n3YMocuMI0yuOcuZjAMnuOSyYPkqAIzQvTFmOsEoy VsQGdXg8OlJ7+1m6IC95pGSnnEQM95/PTiMgnbZ3OBkHIGyKRoTfzb+b7zlVBnvSt+7Z+5AykYB/N mifcABgv6P1ly0s7XJw5UbKewPnWvKWm07RiIEnDn8Rno7DoaCzBLoZPIAjA00yIRFWFR4CN6aSOu imIDKUQ50hNzhUheeSESIVVI9aS/9TJQkhOTEXKiQsMwBH0Xp1nPtSdGdgIoxc7p02uB80SFBscWW 2BcvlQdhiedM9KjCoWHA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1oGmnf-00H43F-VN; Wed, 27 Jul 2022 19:34:32 +0000 Received: from srv4.3e8.eu ([193.25.101.238]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1oGmnB-00H3g3-1z for openwrt-devel@lists.openwrt.org; Wed, 27 Jul 2022 19:34:04 +0000 Received: from localhost.localdomain (p200300c6cf1309a09555e5d92b92c48c.dip0.t-ipconnect.de [IPv6:2003:c6:cf13:9a0:9555:e5d9:2b92:c48c]) (using TLSv1.3 with cipher TLS_CHACHA20_POLY1305_SHA256 (256/256 bits)) (No client certificate requested) by srv4.3e8.eu (Postfix) with ESMTPSA id 1BBAE60211; Wed, 27 Jul 2022 21:33:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=3e8.eu; s=mail20211217; t=1658950421; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=3cdVkrNMx0/HS+QMGTI3yJAUuKLwj20cO7Ul73HB8io=; b=Iw/MzgeEdG7IGyb5wS2igNCPtpswzXRbgHWOIe/hBwZmwDucMtzwG/rO655SBXR8LJ/5Ch l6X2kZ/v3WOMciw2/O8zSaMzHOuqB0jvZ6bNODFVarOi17/MRAgPkVlQ07aMY+rQATdII8 bfF+wqm0ly6W7JS3AChvlEboWxiKLJZGq4id5LrElM2Yb5BcFCVzFf1yJmIPu5nnbt9uqy nwyDrUOUiPfpF81GUhUzLEd0CWfs6AUAXe4SVcJc2hRRyQzj/pJxnOEdOlORa5r92hb88q Uk05fzntdf5I2XlyTRFgLFMEosa+Qiip1qqYQOwH27fO776Z5ZdYwMcfrZFdVw== From: Jan Hoffmann To: openwrt-devel@lists.openwrt.org Cc: Birger Koblitz , Sander Vanheule , Jan Hoffmann Subject: [PATCH firmware-utils v2 1/2] mkh3cimg: add image tool for H3C devices Date: Wed, 27 Jul 2022 21:31:43 +0200 Message-Id: <20220727193144.3782822-2-jan@3e8.eu> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220727193144.3782822-1-jan@3e8.eu> References: <20220727193144.3782822-1-jan@3e8.eu> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220727_123402_139127_B27B8485 X-CRM114-Status: GOOD ( 22.57 ) X-Spam-Score: -0.2 (/) X-Spam-Report: Spam detection software, running on the system "bombadil.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: Firmware images for these devices can contain multiple files, such as application files or bootloader images. This tool only creates images with a single application file. In the case of OpenWrt, this [...] Content analysis details: (-0.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_NONE SPF: sender does not publish an SPF Record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -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 Firmware images for these devices can contain multiple files, such as application files or bootloader images. This tool only creates images with a single application file. In the case of OpenWrt, this is going to contain the kernel image. Compressed files are supported by the image format, in this case the supplied input file needs to be a 7z archive with LZMA compression. Signed-off-by: Jan Hoffmann --- CMakeLists.txt | 1 + src/mkh3cimg.c | 366 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 367 insertions(+) create mode 100644 src/mkh3cimg.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e6592bccb157..daf442681d66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,7 @@ FW_UTIL(mkdniimg "" "" "") FW_UTIL(mkedimaximg "" "" "") FW_UTIL(mkfwimage "" "-Wextra -D_FILE_OFFSET_BITS=64" "${ZLIB_LIBRARIES}") FW_UTIL(mkfwimage2 "" "" "${ZLIB_LIBRARIES}") +FW_UTIL(mkh3cimg "" "" "") FW_UTIL(mkheader_gemtek "" "" "${ZLIB_LIBRARIES}") FW_UTIL(mkhilinkfw "" "" "${OPENSSL_CRYPTO_LIBRARIES}") FW_UTIL(mkmerakifw src/sha1.c "" "") diff --git a/src/mkh3cimg.c b/src/mkh3cimg.c new file mode 100644 index 000000000000..d5660537f196 --- /dev/null +++ b/src/mkh3cimg.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if !defined(__BYTE_ORDER) +#error "Unknown byte order" +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_be16(x) (x) +#define cpu_to_be32(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_be16(x) bswap_16(x) +#define cpu_to_be32(x) bswap_32(x) +#else +#error "Unsupported endianness" +#endif + + +#define IMAGE_VERSION 1 +#define FILE_VERSION 1 +#define FILE_DESCRIPTION "OpenWrt" +#define FILE_TYPE_MASK 0x1 + + +#define PACKAGE_FLAG 2 + +#define FILE_TYPE_APPLICATION 0x04000000 + +#define VERSION_OFFSET_INVALID 0xffffffff + +#define COMPRESSION_TYPE_NONE 0xffffffff +#define COMPRESSION_TYPE_7Z 0x00000002 + + +struct file_header { + uint8_t res1[4]; + + uint32_t header_crc; + + uint32_t file_type; + uint32_t version; + uint32_t product_id; + uint32_t device_id; + + uint32_t length_unpadded; + + uint32_t version_offset; + + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t res2[1]; + uint8_t hour; + uint8_t minute; + uint8_t second; + + uint8_t res3[64]; + + char description[224]; + + uint32_t length; + + uint32_t file_crc; + + uint32_t compression_type; +} __attribute__ ((packed)); + +struct file_desc { + uint32_t file_type; + uint32_t offset; + uint32_t length; + uint32_t file_crc; + uint32_t version; + uint32_t type_mask; +} __attribute__ ((packed)); + +struct image_header { + uint32_t version; + + uint32_t file_count; + + uint32_t product_id; + uint32_t device_id; + + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t res1[1]; + uint8_t hour; + uint8_t minute; + uint8_t second; + + uint16_t package_crc; + uint16_t package_flag; + + uint32_t length; + + struct file_desc files[128]; + + /* RSA signature, not required */ + uint8_t res2[3072]; + + uint32_t header_crc; +} __attribute__ ((packed)); + + +static void *buf; +static size_t buflen; + +static size_t length_unpadded; +static size_t length; + + +static uint32_t crc16_xmodem(char *buf, size_t len) { + uint32_t poly = 0x1021; + uint32_t crc = 0; + char b; + int i, j; + + for (i = 0; i < len; i++) { + b = buf[i]; + crc = crc ^ (b << 8); + + for (j = 0; j < 8; j++) { + crc = crc << 1; + if (crc & 0x10000) { + crc = (crc ^ poly) & 0xffff; + } + } + } + + return crc; +} + +static int create_buffer_and_read_file(char *filename) { + FILE *f; + + f = fopen(filename, "r"); + if (f == NULL) { + fprintf(stderr, "failed to open input file\n"); + goto err; + } + + fseek(f, 0L, SEEK_END); + length_unpadded = ftell(f); + rewind(f); + + length = length_unpadded; + if (length_unpadded % 8 != 0) { + length += 8 - length_unpadded % 8; + } + + buflen = sizeof(struct file_header) + sizeof(struct image_header) + length; + buf = malloc(buflen); + if (!buf) { + fprintf(stderr, "failed to allocate buffer\n"); + goto err_close; + } + + memset(buf, 0, buflen); + + if (fread(buf + sizeof(struct file_header) + sizeof(struct image_header), length_unpadded, 1, f) != 1) { + fprintf(stderr, "failed to read input file\n"); + goto err_free; + } + + fclose(f); + return 0; + +err_free: + free(buf); +err_close: + fclose(f); +err: + return -1; +} + +static void build_file_header(uint32_t product_id, uint32_t device_id, uint32_t compression_type) { + struct file_header *header = buf + sizeof(struct image_header); + uint32_t crc; + + header->file_type = cpu_to_be32(FILE_TYPE_APPLICATION); + + header->version = cpu_to_be32(FILE_VERSION); + + header->product_id = cpu_to_be32(product_id); + header->device_id = cpu_to_be32(device_id); + + header->length_unpadded = cpu_to_be32(length_unpadded); + + header->version_offset = cpu_to_be32(VERSION_OFFSET_INVALID); + + header->year = cpu_to_be16(1970); + header->month = 1; + header->day = 1; + header->hour = 0; + header->minute = 0; + header->second = 0; + + snprintf(header->description, sizeof(header->description), "%s", FILE_DESCRIPTION); + + header->length = cpu_to_be32(length); + + crc = crc16_xmodem(buf + sizeof(struct image_header) + sizeof(struct file_header), length); + header->file_crc = cpu_to_be32(crc); + + header->compression_type = cpu_to_be32(compression_type); + + crc = crc16_xmodem((char *)header + sizeof(header->res1) + sizeof(header->header_crc), + sizeof(struct file_header) - sizeof(header->res1) - sizeof(header->header_crc)); + header->header_crc = cpu_to_be32(crc); +} + +static void build_image_header(uint32_t product_id, uint32_t device_id) { + struct image_header *header = buf; + struct file_header *file_header = buf + sizeof(struct image_header); + uint32_t crc; + + header->version = cpu_to_be32(IMAGE_VERSION); + + header->file_count = cpu_to_be32(1); + + header->product_id = cpu_to_be32(product_id); + header->device_id = cpu_to_be32(device_id); + + header->year = cpu_to_be16(1970); + header->month = 1; + header->day = 1; + header->hour = 0; + header->minute = 0; + header->second = 0; + + crc = crc16_xmodem(buf + sizeof(struct file_header), buflen - sizeof(struct file_header)); + header->package_crc = cpu_to_be16(crc); + header->package_flag = cpu_to_be16(PACKAGE_FLAG); + + header->length = cpu_to_be32(buflen - sizeof(struct image_header)); + + header->files[0].file_type = file_header->file_type; + header->files[0].offset = cpu_to_be32(sizeof(struct image_header)); + header->files[0].length = cpu_to_be32(sizeof(struct file_header) + length); + header->files[0].file_crc = file_header->file_crc; + header->files[0].version = file_header->version; + header->files[0].type_mask = cpu_to_be32(FILE_TYPE_MASK); + + crc = crc16_xmodem((char *)header, sizeof(struct image_header) - sizeof(header->header_crc)); + header->header_crc = cpu_to_be32(crc); +} + +static int write_output_file(char *filename) { + int ret = 0; + FILE *f; + + f = fopen(filename, "w"); + if (f == NULL) { + fprintf(stderr, "failed to open output file\n"); + ret = -1; + goto err; + } + + if (fwrite(buf, buflen, 1, f) != 1) { + fprintf(stderr, "failed to write output file\n"); + ret = -1; + } + + fclose(f); + +err: + return ret; +} + +static void usage(char* argv[]) { + printf("Usage: %s [OPTIONS...]\n" + "\n" + "Options:\n" + " -p product id (32-bit unsigned integer)\n" + " -d device id (32-bit unsigned integer)\n" + " -c compression type of the input file (7z or none)\n" + " (in case of 7z only LZMA compression is allowed)\n" + " -i input filename\n" + " -o output filename\n" + , argv[0]); +} + +int main(int argc, char* argv[]) { + int ret = EXIT_FAILURE; + + static uint32_t product_id = 0; + static uint32_t device_id = 0; + static uint32_t compression_type = COMPRESSION_TYPE_NONE; + static char *input_filename = NULL; + static char *output_filename = NULL; + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "p:d:c:i:o:"); + if (c == -1) + break; + + switch (c) { + case 'p': + product_id = strtoul(optarg, NULL, 0); + break; + case 'd': + device_id = strtoul(optarg, NULL, 0); + break; + case 'c': + if (strcmp(optarg, "none") == 0) { + compression_type = COMPRESSION_TYPE_NONE; + } else if (strcmp(optarg, "7z") == 0) { + compression_type = COMPRESSION_TYPE_7Z; + } else { + usage(argv); + return EXIT_FAILURE; + } + break; + case 'i': + input_filename = optarg; + break; + case 'o': + output_filename = optarg; + break; + default: + usage(argv); + goto err; + } + } + + if (!product_id || !device_id || + !input_filename || strlen(input_filename) == 0 || + !output_filename || strlen(output_filename) == 0) { + + usage(argv); + goto err; + } + + if (create_buffer_and_read_file(input_filename)) { + goto err; + } + + build_file_header(product_id, device_id, compression_type); + + build_image_header(product_id, device_id); + + if (write_output_file(output_filename)) { + goto err_free; + } + + ret = EXIT_SUCCESS; + +err_free: + free(buf); +err: + return ret; +} From patchwork Wed Jul 27 19:31:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Hoffmann X-Patchwork-Id: 1661410 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.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=fnYkqs1u; dkim=fail reason="signature verification failed" (2048-bit key; secure) header.d=3e8.eu header.i=@3e8.eu header.a=rsa-sha256 header.s=mail20211217 header.b=T23v92XA; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.openwrt.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LtPDS0YGpz9sGD for ; Thu, 28 Jul 2022 05:36:56 +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=ahk6Yuk3zQwRIkcFNDCxgMnpsqSjskCqg/Tz/HR6Gas=; b=fnYkqs1uc7o6K9 k4VkGeZNtAlM0cJWLto8fWKkBuf+6GCKRVuEcYrFyDOhlUfuHiNn1iRZjsdWddObB3FtwvlQOdPia N51VuTekKitIwxlE5bo/cep45tJ4/TcWiZOOgRP8jcTft6S3MY2fBiBMJFOOv3u5F18fF6c5m9ejK DpPaBYxaVIDx84Fk7PYvk64GtJmHdAtrIh9jppKG4Ind/qOVVG7fVEgtq5eD5BaQrymbe8tKvLX6v LnVEI1LhnDRNxTpEhB585ZrDRvLnTiVmIvinotnqap7g3YsZG0gSEXVI+PLpnPWSO0ABHIo3HH0QQ T+8Z4GHA22Q2hvXxbqQg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1oGmnR-00H3wA-99; Wed, 27 Jul 2022 19:34:17 +0000 Received: from srv4.3e8.eu ([193.25.101.238]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1oGmn8-00H3gA-0V for openwrt-devel@lists.openwrt.org; Wed, 27 Jul 2022 19:34:01 +0000 Received: from localhost.localdomain (p200300c6cf1309a09555e5d92b92c48c.dip0.t-ipconnect.de [IPv6:2003:c6:cf13:9a0:9555:e5d9:2b92:c48c]) (using TLSv1.3 with cipher TLS_CHACHA20_POLY1305_SHA256 (256/256 bits)) (No client certificate requested) by srv4.3e8.eu (Postfix) with ESMTPSA id 94EBD60212; Wed, 27 Jul 2022 21:33:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=3e8.eu; s=mail20211217; t=1658950421; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Q/T6lobFD8BfOMbIz6A+OUnUYMCNRpVgrPq9DN+oYkc=; b=T23v92XAw/IvbvZlPCWTqlg0EXZppTga/XtE9IOqjHgQlFMpATD8j1b239yq2OGDvoJ0GF hkTsYEsvy9/vVzq4c28PrPLAWKuh2sO54WJVbwOwaAwCHxyz1UJk0vz6l962bXP0RXRLYd OeXrYfjXSfTRPPMePSY81VvbLmpds0nY8x+WdHySxE7+tT1RMLy2hJi/Az/9yhrkjIeVtq 9Kcsaj2SvID3RK3gXdd88baanLO5u7wOf6sAyKxBkfzDu6VrhuMSe1PbhdDYfWSHzpD7L0 eCcEMtR/wfaGw+TgHg8/P5agyrsAKJeAXpmOytWDzc2ILC6V8lrDaFrZQHe4DA== From: Jan Hoffmann To: openwrt-devel@lists.openwrt.org Cc: Birger Koblitz , Sander Vanheule , Jan Hoffmann Subject: [PATCH firmware-utils v2 2/2] mkh3cvfs: add filesystem tool for H3C devices Date: Wed, 27 Jul 2022 21:31:44 +0200 Message-Id: <20220727193144.3782822-3-jan@3e8.eu> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220727193144.3782822-1-jan@3e8.eu> References: <20220727193144.3782822-1-jan@3e8.eu> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220727_123358_881738_8E93591B X-CRM114-Status: GOOD ( 24.69 ) X-Spam-Score: -0.2 (/) X-Spam-Report: Spam detection software, running on the system "bombadil.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 bootloader can only boot images from a custom filesystem which normally spans most of the flash on the device. This tool creates a filesystem image containing a single file (which should be a firm [...] Content analysis details: (-0.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_NONE SPF: sender does not publish an SPF Record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -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 bootloader can only boot images from a custom filesystem which normally spans most of the flash on the device. This tool creates a filesystem image containing a single file (which should be a firmware image created using mkh3cimg). The size of the filesystem is hardcoded in the bootloader. However, an image that is only slightly larger than its content is sufficient, as long as no write operations are performed in the bootloader menu. This allows to append further data (i.e. rootfs) after the end of the filesystem image. Signed-off-by: Jan Hoffmann --- CMakeLists.txt | 1 + src/mkh3cvfs.c | 426 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 427 insertions(+) create mode 100644 src/mkh3cvfs.c diff --git a/CMakeLists.txt b/CMakeLists.txt index daf442681d66..9b70226b8dc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,7 @@ FW_UTIL(mkedimaximg "" "" "") FW_UTIL(mkfwimage "" "-Wextra -D_FILE_OFFSET_BITS=64" "${ZLIB_LIBRARIES}") FW_UTIL(mkfwimage2 "" "" "${ZLIB_LIBRARIES}") FW_UTIL(mkh3cimg "" "" "") +FW_UTIL(mkh3cvfs "" "" "") FW_UTIL(mkheader_gemtek "" "" "${ZLIB_LIBRARIES}") FW_UTIL(mkhilinkfw "" "" "${OPENSSL_CRYPTO_LIBRARIES}") FW_UTIL(mkmerakifw src/sha1.c "" "") diff --git a/src/mkh3cvfs.c b/src/mkh3cvfs.c new file mode 100644 index 000000000000..e5b3f353c920 --- /dev/null +++ b/src/mkh3cvfs.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if !defined(__BYTE_ORDER) +#error "Unknown byte order" +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_be16(x) (x) +#define cpu_to_be32(x) (x) +#define be16_to_cpu(x) (x) +#define be32_to_cpu(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_be16(x) bswap_16(x) +#define cpu_to_be32(x) bswap_32(x) +#define be16_to_cpu(x) bswap_16(x) +#define be32_to_cpu(x) bswap_32(x) +#else +#error "Unsupported endianness" +#endif + + +#define FAT_PTR_FLAGS_GET(x) ((be32_to_cpu(x) & 0xff000000) >> 24) +#define FAT_PTR_FLAGS_SET(x, y) (x = cpu_to_be32((be32_to_cpu(x) & 0x00ffffff) | ((y & 0x000000ff) << 24))) + +#define FAT_PTR_VAL_GET(x) (be32_to_cpu(x) & 0x00ffffff) +#define FAT_PTR_VAL_SET(x, y) (x = cpu_to_be32((be32_to_cpu(x) & 0xff000000) | (y & 0x00ffffff))) + + +struct fat_entry { + /* first byte contains flags */ + uint32_t previous; + + /* first byte is reserved */ + uint32_t next; +} __attribute__ ((packed)); + +struct file_entry { + uint8_t flags; + + uint8_t res0[5]; + + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + + uint8_t res1[3]; + + uint32_t length; + + uint32_t parent_block; + uint16_t parent_index; + + uint8_t res2[2]; + + uint32_t data_block; + + char name[96]; +} __attribute__ ((packed)); + + +#define ERASEBLOCK_SIZE 0x10000 +#define BLOCK_SIZE 0x400 + +#define BLOCKS_PER_ERASEBLOCK (ERASEBLOCK_SIZE / BLOCK_SIZE) +#define FILE_ENTRIES_PER_BLOCK (BLOCK_SIZE / sizeof(struct file_entry)) + + +#define FLAG_FREE 0x80 +#define FLAG_VALID 0x40 +#define FLAG_INVALID 0x20 +#define FLAG_HIDE 0x10 +#define FLAG_DIRECTORY 0x02 +#define FLAG_READONLY 0x01 + + +static FILE *f; +static size_t file_size = 0; + +static int dir_block = 1; +static int dir_count = 0; + +static int next_data_block = 2; + + +static inline size_t fat_entry_offset(int block) { + return ERASEBLOCK_SIZE * (block / (BLOCKS_PER_ERASEBLOCK-1)) + + sizeof(struct fat_entry) * (block % (BLOCKS_PER_ERASEBLOCK-1)); +} + +static inline size_t block_offset(int block) { + return ERASEBLOCK_SIZE * (block / (BLOCKS_PER_ERASEBLOCK-1)) + + BLOCK_SIZE * (1 + (block % (BLOCKS_PER_ERASEBLOCK-1))); +} + +static int init_eraseblock(size_t offset) { + size_t end = offset - (offset % ERASEBLOCK_SIZE) + ERASEBLOCK_SIZE; + char *fill = "\xff"; + int i; + + while (file_size < end) { + if (fseek(f, file_size, SEEK_SET)) { + fprintf(stderr, "failed to seek to end\n"); + return -1; + } + + for (i = 0; i < ERASEBLOCK_SIZE; i++) { + if (fwrite(fill, 1, 1, f) != 1) { + fprintf(stderr, "failed to write eraseblock\n"); + return -1; + } + } + + file_size += ERASEBLOCK_SIZE; + } + + return 0; +} + +static inline void init_fat_entry(struct fat_entry *out) { + memset(out, '\xff', sizeof(struct fat_entry)); +} + +static int read_fat_entry(struct fat_entry *out, int block) { + size_t offset = fat_entry_offset(block); + + if (init_eraseblock(offset)) { + return -1; + } + + if (fseek(f, offset, SEEK_SET)) { + fprintf(stderr, "failed to seek to fat entry\n"); + return -1; + } + + if (fread(out, sizeof(struct fat_entry), 1, f) != 1) { + fprintf(stderr, "failed to read fat entry\n"); + return -1; + } + + return 0; +} + +static int write_fat_entry(struct fat_entry *in, int block) { + size_t offset = fat_entry_offset(block); + + if (init_eraseblock(offset)) { + return -1; + } + + if (fseek(f, offset, SEEK_SET)) { + fprintf(stderr, "failed to seek to fat entry\n"); + return -1; + } + + if (fwrite(in, sizeof(struct fat_entry), 1, f) != 1) { + fprintf(stderr, "failed to write fat entry\n"); + return -1; + } + + return 0; +} + +static inline void init_file_entry(struct file_entry *out) { + memset(out, '\xff', sizeof(struct file_entry)); +} + +static int write_file_entry(struct file_entry *in, int block, int index) { + size_t offset = block_offset(block) + sizeof(struct file_entry) * index; + + if (init_eraseblock(offset)) { + return -1; + } + + if (fseek(f, offset, SEEK_SET)) { + fprintf(stderr, "failed to seek to file entry\n"); + return -1; + } + + if (fwrite(in, sizeof(struct file_entry), 1, f) != 1) { + fprintf(stderr, "failed to write file entry\n"); + return -1; + } + + return 0; +} + +static int write_block(void *in, size_t len, int block) { + size_t offset = block_offset(block); + + if (init_eraseblock(offset)) { + return -1; + } + + if (fseek(f, offset, SEEK_SET)) { + fprintf(stderr, "failed to seek to block\n"); + return -1; + } + + if (fwrite(in, len, 1, f) != 1) { + fprintf(stderr, "failed to write block\n"); + return -1; + } + + return 0; +} + +static int create_root_directory() { + struct fat_entry fat; + struct file_entry file; + + /* write format flag / FAT entry for block 0 (contains root file entry) */ + init_fat_entry(&fat); + fat.previous = cpu_to_be32((ERASEBLOCK_SIZE << 12) | BLOCK_SIZE); + if (write_fat_entry(&fat, 0)) { + return -1; + } + + /* write root file entry */ + init_file_entry(&file); + file.flags = ~(FLAG_FREE | FLAG_VALID) & 0xff; + file.parent_block = cpu_to_be32(0); + file.data_block = cpu_to_be32(1); + if (write_file_entry(&file, 0, 0)) { + return -1; + } + + /* write FAT entry for block 1 (contains first file entries of root directory) */ + init_fat_entry(&fat); + FAT_PTR_FLAGS_SET(fat.previous, ~(FLAG_FREE | FLAG_VALID)); + if (write_fat_entry(&fat, 1)) { + return -1; + } + + return 0; +} + +static int write_file(char *name, char *path) { + int ret = -1; + struct fat_entry fat; + struct file_entry file; + FILE *fin; + char buf[BLOCK_SIZE]; + size_t len; + size_t total = 0; + int first_data_block = next_data_block; + int data_block = 0; + + fin = fopen(path, "r"); + if (fin == NULL) { + fprintf(stderr, "failed to open input file\n"); + return ret; + } + + while ((len = fread(buf, 1, BLOCK_SIZE, fin)) != 0 || !data_block) { + total += len; + + /* update next pointer of previous FAT entry */ + if (data_block) { + if (read_fat_entry(&fat, data_block)) { + goto err; + } + FAT_PTR_VAL_SET(fat.next, next_data_block); + if (write_fat_entry(&fat, data_block)) { + goto err; + } + } + + /* write FAT entry for new block */ + init_fat_entry(&fat); + FAT_PTR_FLAGS_SET(fat.previous, ~(FLAG_FREE | FLAG_VALID)); + if (data_block) { + FAT_PTR_VAL_SET(fat.previous, data_block); + } + if (write_fat_entry(&fat, next_data_block)) { + goto err; + } + + /* write data block */ + if (write_block(buf, len, next_data_block)) { + goto err; + } + + data_block = next_data_block; + next_data_block++; + } + + /* create new file entries block if necessary */ + if (dir_count == FILE_ENTRIES_PER_BLOCK) { + /* update next pointer of previous FAT entry */ + if (read_fat_entry(&fat, dir_block)) { + goto err; + } + FAT_PTR_VAL_SET(fat.next, next_data_block); + if (write_fat_entry(&fat, dir_block)) { + goto err; + } + + /* write FAT entry for new block */ + init_fat_entry(&fat); + FAT_PTR_FLAGS_SET(fat.previous, ~(FLAG_FREE | FLAG_VALID)); + FAT_PTR_VAL_SET(fat.previous, dir_block); + if (write_fat_entry(&fat, next_data_block)) { + goto err; + } + + dir_block = next_data_block; + dir_count = 0; + next_data_block++; + } + + /* write file entry */ + init_file_entry(&file); + + file.flags = ~(FLAG_FREE | FLAG_VALID) & 0xff; + + file.year = cpu_to_be16(1970); + file.month = 1; + file.day = 1; + file.hour = 0; + file.minute = 0; + file.second = 0; + + file.length = cpu_to_be32(total); + + file.parent_block = cpu_to_be32(0); + file.parent_index = cpu_to_be16(0); + + file.data_block = cpu_to_be32(first_data_block); + + snprintf(file.name, sizeof(file.name), "%s", name); + + if (write_file_entry(&file, dir_block, dir_count)) { + goto err; + } + + dir_count++; + + ret = 0; +err: + fclose(fin); + return ret; +} + +static void usage(char* argv[]) { + printf("Usage: %s [OPTIONS...]\n" + "\n" + "Options:\n" + " -f filename in image\n" + " -i input filename\n" + " -o output filename\n" + , argv[0]); +} + +int main(int argc, char* argv[]) { + int ret = EXIT_FAILURE; + + static char *filename = NULL; + static char *input_filename = NULL; + static char *output_filename = NULL; + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "f:i:o:"); + if (c == -1) + break; + + switch (c) { + case 'f': + filename = optarg; + break; + case 'i': + input_filename = optarg; + break; + case 'o': + output_filename = optarg; + break; + default: + usage(argv); + goto err; + } + } + + if (!filename || strlen(filename) == 0 || + !input_filename || strlen(input_filename) == 0 || + !output_filename || strlen(output_filename) == 0) { + + usage(argv); + goto err; + } + + f = fopen(output_filename, "w+"); + if (f == NULL) { + fprintf(stderr, "failed to open output file\n"); + goto err; + } + + if (create_root_directory()) { + goto err_close; + } + + if (write_file(filename, input_filename)) { + goto err_close; + } + + ret = EXIT_SUCCESS; + +err_close: + fclose(f); +err: + return ret; +}