From patchwork Wed Jun 21 07:29:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?UmFmYcWCIE1pxYJlY2tp?= X-Patchwork-Id: 1797680 X-Patchwork-Delegate: zajec5@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.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=) Authentication-Results: legolas.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=r1KOoYvy; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20221208 header.b=fa9gww3b; dkim-atps=neutral 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 ECDSA (P-384) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QmFZ21Q3nz20XS for ; Wed, 21 Jun 2023 17:31: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: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:In-Reply-To:References: List-Owner; bh=M5n4dlKA910iDLEkuXr0IaVPrjhsk/7V2RArsheqv8w=; b=r1KOoYvyKp3kLp nM/ZKN0NEqMASBgyJ9Gc0ATrNjc0FexKuIs/GLKPvhV3vbqW2j6O5gL7hko0WbsSTCGPxXLte8Fmv woBs4pVaAVVa/eBE11gBZj5FlpvEhxdwIlaqNZ0tAGPlGXN2cuXPh3dbjWwql0G1oJmEwo2ZP9qk2 +6AGU7+cSxg3ro6euHD0ytLdS/aebP/5IOInUU9tybKgKV6o5NNHijkVMJl2ZKi1VyJELy4x2kDFv k5bQcL51tB39ASUY1eSJASuWgdzPotgYHKBjWXFt4ZYETpt2vIhnmQwfLPeXzO+TUK4zN7QrZ82qL p1Llp4zGQY9MeI2pTLAQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qBsHC-00DYDY-2f; Wed, 21 Jun 2023 07:29:14 +0000 Received: from mail-ej1-x62d.google.com ([2a00:1450:4864:20::62d]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1qBsH8-00DYCm-2w for openwrt-devel@lists.openwrt.org; Wed, 21 Jun 2023 07:29:12 +0000 Received: by mail-ej1-x62d.google.com with SMTP id a640c23a62f3a-9896216338cso104622966b.3 for ; Wed, 21 Jun 2023 00:29:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1687332548; x=1689924548; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=oXTBqvL0Xd5QziPZAIbirmFsItu9m3YBm31TgF7KT4A=; b=fa9gww3b0FdyvpWESH2J4Tfiy+vfOaKnlrk+o530WHGWYHR+/r2JaYRJmOuiV2n0Kt M2rIouRfY7SUU/gshaVVpHsPdZA+ES23Pviu58wHtQqYXBBL5qD8MEcpr2GTrlI0x2CF 2L5sKP8pCsN3bSJp6499O1l8eXdfd3o63TY/q71wv5txLUVX32EvrQ4k2oe7SgpQozHx 0gcEtT/Cb/3wry2svXotNINKqZtmZfHJVAMYt/nXr2rbi0qg0kI1id6+II3dgxuSsGZD JflpxP4AbGs65wBNFcrl57ecySjTBnM+0dLM1e4pnuOTGpbwUfBl9JcCb6WAr7DA/dOI m4dw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687332548; x=1689924548; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=oXTBqvL0Xd5QziPZAIbirmFsItu9m3YBm31TgF7KT4A=; b=IjjZ9nvlcLfbSv96IF1xwJvQ5A5LDu7Xh/vJ56fxCptOAzEIBBtd5jlRYCbt5oJe4F pL/B5yeH+K9axocMssgPjIG3kFTsVVOzLRyCjQtrr3aF6Ol0YNvA/MMmoXdLZ6eKAUhS fmX1iinVN8mq0+gqL9sWgJc99yOr4fDKh09Fmnoq2Gt8FFNxNR/Q/LtZmiHFsXvTy8nr yHbP+4CAuOwdyk1D2tRJFHUFUGJt1+jZUpn5LpiARw9nEEBhC8+g/ddsXYxEAd+Aogjy Jp9KECLUyKznmcXEX/NBHKYjhQj+HhqTguS50VjhYsGacdFlCXzH2+Kp7kWH8bJ/wPEW Xy5A== X-Gm-Message-State: AC+VfDw/vxkLaNKNxwLKpu//Dw1Us6ca0CqJfY1ztYPDwp5yTJlcfcrf B1NfzLhd2PhEUo5LQkLVnnB4iSdmnI4= X-Google-Smtp-Source: ACHHUZ54cXIi6bWjg3RZE244M2pQ6E8zzXs3nH/i5t9VBkBk9GsHunXLjSZ4LxXOwIT/fy3/lltzlA== X-Received: by 2002:a17:907:320a:b0:965:d18b:f03a with SMTP id xg10-20020a170907320a00b00965d18bf03amr13212099ejb.58.1687332548101; Wed, 21 Jun 2023 00:29:08 -0700 (PDT) Received: from localhost.lan (031011218106.poznan.vectranet.pl. [31.11.218.106]) by smtp.gmail.com with ESMTPSA id u13-20020a170906b10d00b00988955f7b5esm2608833ejy.157.2023.06.21.00.29.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Jun 2023 00:29:07 -0700 (PDT) From: =?utf-8?b?UmFmYcWCIE1pxYJlY2tp?= To: openwrt-devel@lists.openwrt.org Cc: =?utf-8?b?UmFmYcWCIE1pxYJlY2tp?= Subject: [PATCH firmware-utils] bcmclm: new tool for reading Broadcom's CLM data Date: Wed, 21 Jun 2023 09:29:00 +0200 Message-Id: <20230621072900.20652-1-zajec5@gmail.com> X-Mailer: git-send-email 2.35.3 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230621_002910_949709_016D75F8 X-CRM114-Status: GOOD ( 20.31 ) X-Spam-Score: 0.1 (/) 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_Rafa=C5=82_Mi=C5=82ecki_Broadc?= =?unknown-8bit?q?om_developed_CLM_format_for_devices?= =?unknown-8bit?q?_vendors_to_provide_regulatory_configuration_independently_?= =?unknown-8bit?q?of_executable_firmware=2E?= =?unknown-8bit?q?_They_contain_info_about_countries=2C_channels_and_rates=2E?= =?unknown-8bit?q?_?= =?unknown-8bit?q?_?= =?unknown-8bit?q?_Content_analysis_details=3A___=280=2E1_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_RCVD=5FIN=5FDNSWL=5FNONE_____RBL=3A_Sender_listed_a?= =?unknown-8bit?q?t_https=3A//www=2Ednswl=2Eorg/=2C?= =?unknown-8bit?q?_no_trust?= =?unknown-8bit?b?IFsyYTAwOjE0NTA6NDg2NDoyMDowOjA6MDo2MmQgbGlzdGVkIGluXQ==?= =?unknown-8bit?b?IFtsaXN0LmRuc3dsLm9yZ10=?= =?unknown-8bit?q?_-0=2E0_SPF=5FPASS_______________SPF=3A_sender_matches_SPF_?= =?unknown-8bit?q?record?= =?unknown-8bit?q?_0=2E0_SPF=5FHELO=5FNONE__________SPF=3A_HELO_does_not_publ?= =?unknown-8bit?q?ish_an_SPF_Record?= =?unknown-8bit?q?_0=2E0_FREEMAIL=5FFROM__________Sender_email_is_commonly_ab?= =?unknown-8bit?q?used_enduser_mail?= =?unknown-8bit?q?_provider?= =?unknown-8bit?q?_=5Bzajec5=5Bat=5Dgmail=2Ecom=5D?= =?unknown-8bit?q?_0=2E2_FREEMAIL=5FENVFROM=5FEND=5FDIGIT_Envelope-from_freem?= =?unknown-8bit?q?ail_username_ends?= =?unknown-8bit?q?_in_digit?= =?unknown-8bit?q?_=5Bzajec5=5Bat=5Dgmail=2Ecom=5D?= =?unknown-8bit?q?_-0=2E1_DKIM=5FVALID=5FAU__________Message_has_a_valid_DKIM?= =?unknown-8bit?q?_or_DK_signature_from?= =?unknown-8bit?q?_author=27s_domain?= =?unknown-8bit?q?_-0=2E1_DKIM=5FVALID_____________Message_has_at_least_one_v?= =?unknown-8bit?q?alid_DKIM_or_DK_signature?= =?unknown-8bit?q?_0=2E1_DKIM=5FSIGNED____________Message_has_a_DKIM_or_DK_si?= =?unknown-8bit?q?gnature=2C_not_necessarily?= =?unknown-8bit?q?_valid?= =?unknown-8bit?q?_-0=2E1_DKIM=5FVALID=5FEF__________Message_has_a_valid_DKIM?= =?unknown-8bit?q?_or_DK_signature_from?= =?unknown-8bit?q?_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 From: Rafał Miłecki Broadcom developed CLM format for devices vendors to provide regulatory configuration independently of executable firmware. They contain info about countries, channels and rates. CLM data can be packaged with chipset info as CLM BLOB and uploaded to FullMAC firmware for complete channel support and maximum transmission power. CLM binary format is a bit tricky. It consists of a lot of binary structs with all kind of numbers and flags. In-file addresses of those structs and then stored in parent structs. This tool provides initial support for reading CLM data content. It parses the root struct and follows info lower level structs. Signed-off-by: Rafał Miłecki --- CMakeLists.txt | 1 + src/bcmclm.c | 337 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 338 insertions(+) create mode 100644 src/bcmclm.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 31e0a06..2d27227 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ FW_UTIL(avm-wasp-checksum "" --std=gnu99 "") FW_UTIL(bcm4908asus "" "" "") FW_UTIL(bcm4908kernel "" "" "") FW_UTIL(bcmblob "" "" "") +FW_UTIL(bcmclm "" "" "") FW_UTIL(buffalo-enc src/buffalo-lib.c "" "") FW_UTIL(buffalo-tag src/buffalo-lib.c "" "") FW_UTIL(buffalo-tftp src/buffalo-lib.c "" "") diff --git a/src/bcmclm.c b/src/bcmclm.c new file mode 100644 index 0000000..ac8eacf --- /dev/null +++ b/src/bcmclm.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Rafał Miłecki + */ + +#include +#include +#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_le32(x) bswap_32(x) +#define le32_to_cpu(x) bswap_32(x) +#define cpu_to_be32(x) (x) +#define be32_to_cpu(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_le32(x) (x) +#define le32_to_cpu(x) (x) +#define cpu_to_be32(x) bswap_32(x) +#define be32_to_cpu(x) bswap_32(x) +#else +#error "Unsupported endianness" +#endif + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define BCMCLM_MAGIC "CLM DATA" + +/* Raw data */ + +struct bcmclm_header { + char magic[8]; + uint32_t unk0; + uint8_t unk1[2]; + char api[20]; + char compiler[10]; + uint32_t virtual_header_address; + uint32_t lookup_table_address; + char clm_import_ver[30]; + char manufacturer[22]; +}; + +struct bcmclm_lookup_table { + uint32_t offset0; + uint32_t offset1; + uint32_t offset2; + uint32_t offset3; + uint32_t offset4; + uint32_t offset5; + uint32_t offset6; + uint32_t offset7; + uint32_t offset8; + uint32_t offset9; + uint32_t offset10; + uint32_t offset11; + uint32_t offset12; + uint32_t offset13; + uint32_t offset14; + uint32_t offset15; + uint32_t offset16; + uint32_t offset17; + uint32_t offset18; + uint32_t offset19; + uint32_t offset20; + uint32_t offset21; + uint32_t offset22; + uint32_t offset23; + uint32_t offset_creation_date; + uint32_t offset25; + uint32_t offset26; + uint32_t offset27; + uint32_t offset28; + uint32_t offset29; + uint32_t offset30; + uint32_t offset31; + uint32_t offset32; + uint32_t offset33; + uint32_t offset34; + uint32_t offset35; + uint32_t offset36; + uint32_t offset37; + uint32_t offset38; + uint32_t offset39; + uint32_t offset40; + uint32_t offset41; + uint32_t offset42; + uint32_t offset43; + uint32_t offset44; + uint32_t offset45; + uint32_t offset46; + uint32_t offset47; +}; + +/* Parsed info */ + +struct bcmclm_info { + struct bcmclm_header header; + struct bcmclm_lookup_table lookup_table; + size_t file_size; + size_t clm_offset; + size_t offsets_fixup; +}; + +static inline size_t bcmclm_min(size_t x, size_t y) +{ + return x < y ? x : y; +} + +/************************************************** + * Helpers + **************************************************/ + +static FILE *bcmclm_open(const char *pathname, const char *mode) +{ + struct stat st; + + if (pathname) + return fopen(pathname, mode); + + if (isatty(fileno(stdin))) { + fprintf(stderr, "Reading from TTY stdin is unsupported\n"); + return NULL; + } + + if (fstat(fileno(stdin), &st)) { + fprintf(stderr, "Failed to fstat stdin: %d\n", -errno); + return NULL; + } + + if (S_ISFIFO(st.st_mode)) { + fprintf(stderr, "Reading from pipe stdin is unsupported\n"); + return NULL; + } + + return stdin; +} + +static void bcmclm_close(FILE *fp) +{ + if (fp != stdin) + fclose(fp); +} + +/************************************************** + * Existing CLM parser + **************************************************/ + +static int bcmclm_search(FILE *fp, struct bcmclm_info *info) +{ + uint8_t buf[1024]; + size_t offset = 0; + size_t bytes; + int i; + + while ((bytes = fread(buf, 1, sizeof(buf), fp)) == sizeof(buf)) { + for (i = 0; i < bytes - 12; i += 4) { + uint32_t unk = le32_to_cpu(*(uint32_t *)(&buf[i + 8])); + + if (!memcmp(&buf[i], BCMCLM_MAGIC, 8) && !(unk & 0xff00ffff)) { + info->clm_offset = offset + i; + + printf("Found CLM at offset 0x%zx\n", info->clm_offset); + printf("\n"); + + return 0; + } + } + + offset += bytes; + } + + return -ENOENT; +} + +static int bcmclm_parse(FILE *fp, struct bcmclm_info *info) +{ + struct bcmclm_header *header = &info->header; + struct bcmclm_lookup_table *lookup_table = &info->lookup_table; + struct stat st; + int err = 0; + + /* File size */ + + if (fstat(fileno(fp), &st)) { + err = -errno; + fprintf(stderr, "Failed to fstat: %d\n", err); + return err; + } + info->file_size = st.st_size; + + /* Header */ + + fseek(fp, info->clm_offset, SEEK_SET); + + if (fread(header, 1, sizeof(*header), fp) != sizeof(*header)) { + fprintf(stderr, "Failed to read CLM header\n"); + return -EIO; + } + + if (strncmp(header->magic, BCMCLM_MAGIC, 8)) { + fprintf(stderr, "Invalid CLM header magic\n"); + return -EPROTO; + } + + info->offsets_fixup = info->clm_offset - le32_to_cpu(header->virtual_header_address); + + /* Lookup table */ + + fseek(fp, le32_to_cpu(info->header.lookup_table_address) + info->offsets_fixup, SEEK_SET); + + if (fread(lookup_table, 1, sizeof(*lookup_table), fp) != sizeof(*lookup_table)) { + fprintf(stderr, "Failed to read lookup table\n"); + return -EIO; + } + + return 0; +} + +/************************************************** + * Info + **************************************************/ + +static void bcmclm_print_lookup_data(FILE *fp, struct bcmclm_info *info) +{ + uint8_t buf[64]; + size_t bytes; + + if (info->lookup_table.offset_creation_date) { + printf("\n"); + + fseek(fp, le32_to_cpu(info->lookup_table.offset_creation_date) + info->offsets_fixup, SEEK_SET); + + bytes = fread(buf, 1, sizeof(buf), fp); + if (bytes) { + printf("Creation date: %s\n", buf); + } + } +} + +static int bcmclm_info(int argc, char **argv) +{ + struct bcmclm_info info = {}; + const char *pathname = NULL; + int search = 0; + FILE *fp; + int c; + int err = 0; + + while ((c = getopt(argc, argv, "i:s")) != -1) { + switch (c) { + case 'i': + pathname = optarg; + break; + case 's': + search = 1; + break; + } + } + + fp = bcmclm_open(pathname, "r"); + if (!fp) { + fprintf(stderr, "Failed to open CLM\n"); + err = -EACCES; + goto out; + } + + if (search) { + err = bcmclm_search(fp, &info); + if (err) { + fprintf(stderr, "Failed to find CLM in input file\n"); + goto err_close; + } + } + + err = bcmclm_parse(fp, &info); + if (err) { + fprintf(stderr, "Failed to parse CLM\n"); + goto err_close; + } + + printf("API: %s\n", info.header.api); + printf("Compiler: %s\n", info.header.compiler); + printf("clm_import_ver: %s\n", info.header.clm_import_ver); + printf("Manufacturer: %s\n", info.header.manufacturer); + printf("\n"); + printf("Virtual header address: 0x%08x (real: 0x%zx)\n", le32_to_cpu(info.header.virtual_header_address), le32_to_cpu(info.header.virtual_header_address) + info.offsets_fixup); + printf("Virtual lookup table address: 0x%08x (real: 0x%zx)\n", le32_to_cpu(info.header.lookup_table_address), le32_to_cpu(info.header.lookup_table_address) + info.offsets_fixup); + + bcmclm_print_lookup_data(fp, &info); + +err_close: + bcmclm_close(fp); +out: + return err; +} + +/************************************************** + * Start + **************************************************/ + +static void usage() +{ + printf("Usage:\n"); + printf("\n"); + printf("Info about CLM:\n"); + printf("\tbcmclm info \n"); + printf("\t-i \t\t\t\t\tinput CLM\n"); + printf("\t-s\t\t\t\t\tsearch for CLM data in bigger file\n"); + printf("\n"); + printf("Examples:\n"); + printf("\tbcmclm info -i x.clm\n"); + printf("\tbcmclm info -s -i brcmfmac4366c-pcie.bin\n"); +} + +int main(int argc, char **argv) +{ + if (argc > 1) { + optind++; + if (!strcmp(argv[1], "info")) + return bcmclm_info(argc, argv); + } + + usage(); + + return 0; +}