From patchwork Fri Jun 16 11:34:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Herbrechtsmeier X-Patchwork-Id: 1795870 X-Patchwork-Delegate: xypron.glpk@gmx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=weidmueller.onmicrosoft.com header.i=@weidmueller.onmicrosoft.com header.a=rsa-sha256 header.s=selector1-weidmueller-onmicrosoft-com header.b=xuzGKsey; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QjHD40l8vz20X8 for ; Fri, 16 Jun 2023 21:36:04 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id EEEE88623E; Fri, 16 Jun 2023 13:35:00 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=weidmueller.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=weidmueller.onmicrosoft.com header.i=@weidmueller.onmicrosoft.com header.b="xuzGKsey"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 129A4847B6; Fri, 16 Jun 2023 13:34:48 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,FORGED_SPF_HELO,SPF_HELO_PASS,T_SCC_BODY_TEXT_LINE, T_SPF_PERMERROR autolearn=no autolearn_force=no version=3.4.2 Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on20621.outbound.protection.outlook.com [IPv6:2a01:111:f400:7d00::621]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 81D2586245 for ; Fri, 16 Jun 2023 13:34:41 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=weidmueller.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=stefan.herbrechtsmeier-oss@weidmueller.com ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Utx8rdUBzh6wH4VInzFwpS4pmvkp3APRc68CNZAdDREzvxpsfM+/fpNWqxDRDg6GdoNj7zD2An+T9Hp+8/fgXTaWBllNP9o20uAEL0uKZzavK7o02e13Yw5OGm2I8cRGxqHKYiKv8joViTQlNs1XP+DhQ9F69ChY04m8ZUkTiQkgAL2kc8Rl27yN6Q8j2vr6BZ7G4JbF43JaRQWzYlpt2/lqq0k71QxrpO5f1XiBqVrLAoRYZ82rKe11ej3aiomkmzgfyeH87voXMdME1aqXdafgrVo0Nw2jyK9vpvj99SHGX4zUB7SWHzIPzfSgLTItBa0GSnNmqCtvrAf78PYYGg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=JyYiMTHeKofXMBar6gH6M/J0xVNQy5jpEpe6eDIG3XI=; b=ktFhaFdUZWyMusIJqxOTs/DHiFG0BY7xZLV9oAPbzJm3l3S8EzNxCUjBQXv3QcweWxfNaZGnZRtZnssGRpfN5gNhOl7rUxqZbQnb1V0loXNCWM12RT/Qxz0ol3APCtzD1GozyNqE9vTBIq6OynpEAQt7vToFeoR+YXWNJeRR6s7hhPTZbEVSn/hMGdVvVyqMP56usuFsdZ1ZXY2bCI25EGCqoD3XsjMbSI5Avb0Qdy5E11qHCe5rquM6uk75WPVlaAUU3X4Un78YKxR6mFWt5D8ybj2sRJJ/W7u96EtsgZbqeu8PHwu4fWUs4gwnmsloMaQx7ddouEzgqsKjos2V2w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=weidmueller.com; dmarc=pass action=none header.from=weidmueller.com; dkim=pass header.d=weidmueller.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=weidmueller.onmicrosoft.com; s=selector1-weidmueller-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=JyYiMTHeKofXMBar6gH6M/J0xVNQy5jpEpe6eDIG3XI=; b=xuzGKseyRUo4dd8avOTUpMmPKm1oB4QYqVLgOuvq8sHeDg93bLhsNj295laAuvzySe+AChaWA+vItNhmWngaVpIlisyIaW3CJKsaePF3YJFrUgZVTyL/lg/qiwgqlP6d9aeDzWYrce7eUACB13c630aboGGnrO+l3UCLFjFBECE= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=weidmueller.com; Received: from PAXPR08MB6969.eurprd08.prod.outlook.com (2603:10a6:102:1d8::23) by DB9PR08MB6444.eurprd08.prod.outlook.com (2603:10a6:10:23c::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6500.29; Fri, 16 Jun 2023 11:34:40 +0000 Received: from PAXPR08MB6969.eurprd08.prod.outlook.com ([fe80::7501:6f07:8ffe:f389]) by PAXPR08MB6969.eurprd08.prod.outlook.com ([fe80::7501:6f07:8ffe:f389%4]) with mapi id 15.20.6500.025; Fri, 16 Jun 2023 11:34:39 +0000 From: Stefan Herbrechtsmeier To: u-boot@lists.denx.de Cc: Malte Schmidt , Sughosh Ganu , Malte Schmidt , Stefan Herbrechtsmeier , Etienne Carriere , Heinrich Schuchardt , Ilias Apalodimas , Masahisa Kojima Subject: [PATCH 2/5] mkeficapsule: add support for multiple payloads inside capsule Date: Fri, 16 Jun 2023 13:34:23 +0200 Message-Id: <20230616113426.13976-3-stefan.herbrechtsmeier-oss@weidmueller.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20230616113426.13976-1-stefan.herbrechtsmeier-oss@weidmueller.com> References: <20230616113426.13976-1-stefan.herbrechtsmeier-oss@weidmueller.com> X-ClientProxiedBy: FR2P281CA0174.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:9f::11) To PAXPR08MB6969.eurprd08.prod.outlook.com (2603:10a6:102:1d8::23) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR08MB6969:EE_|DB9PR08MB6444:EE_ X-MS-Office365-Filtering-Correlation-Id: 83d34cd9-6e3e-4010-23cb-08db6e5daba1 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 5GSiXCeCIiZcVmWLVDYz+2oFeD97FDsXYcJarlOb/sCpfYzvZ2a4P6osQbB6nA0cA/n6qhW23IA7Xmp8n94gpPbec/rNA5QpD9F1yxIhvQ9BdT4hrNw99m2hnYdn+XNLE0aKhxl1u6TjknCwrrnwhuuE2nX6Vl5lUbn1XnnQoVGMvpwbFGOJvbcnhwfrxipKp4lJJdQFi+K6oVU1sJrC7yWntfx1YClCaKEoX9054RVT2zPlTgvLzxHBZrVcll6C3jDfaEO/Q5RDz8kNzfBZpOC/4KTI+JgkNCuYUzX5T6e+jkOV+rxbYlcJ78cqUfrmaKW783TIb2iyt7D9U0ffmI/jSGVoyqn42oa76JljKuVQoFrVygxpqRRgbUXIp+6tdJplLkPghiPUx55pX/WESwLUAftKA6QdIu4ld1yrwpn+DQJh2Z++8sREoy4AIpbDgIJxwvx7Qp7n5C94DQNW2YV4HYdaDcdrjuuh5eZo/g+iXIQi5fCubUPefDFBYNlKjdmmOeiUUJu3/61N7ApcanCv8f4/zoxVBZ17fQ7c7ZDZWY1YKRnS5N4YVmokgcWisqFc/16gT65DRdpjSDc0ucsKNsT1rFTw+hAJG4qD17ceoENl0obrYxTjMSNhMJBu X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:PAXPR08MB6969.eurprd08.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230028)(4636009)(136003)(346002)(366004)(39860400002)(376002)(396003)(451199021)(5660300002)(54906003)(41300700001)(6486002)(8676002)(8936002)(316002)(6666004)(966005)(52116002)(186003)(6506007)(26005)(6512007)(30864003)(478600001)(4326008)(6916009)(66556008)(66476007)(66946007)(86362001)(1076003)(38100700002)(38350700002)(2616005)(83380400001)(36756003)(2906002)(309714004); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: KKLKWS+WADEmewPWaG4w6nLSwJDbXRc62nGATfUMddGsJMgxnoNBm02aAF2PqpAvSkrklUYwqbbKyKXdQmKGNySax2pNxCVNtWpbxZhK3EMW6+NVaDkksuxzI4JVvWeP9ypfPwnP9VI2/0up1V3G17rblXUUM1vGNtwXTa6kzavfdHAiiwXCBVKeLeJmYdqPu1i5skvxJYGU1DWgWJu1xMk//EEVgfdHHkEWnJbIjmE9H31L/w09Uw/04e/kPk1diEeXT5ZCQWNxF9DJ9R0q/oOGMgBozEYXn5IcrNb4VNAk5Jm0RyAscE6hcSAWyoiYRTBkUXCq1KeE2g5cmzscxF9hkztH4r1XA+mbvEiyXbd/9VjOfWP1EnETc3rUCmPO0DMOdQppgXX3xQzgofBa08iWigUVsPLK3Dt9eX0XTXIJOgoUSifXhX8ScqakaZD5PCDlz1gBn9wzWWlrBf3X/QmL+8ctXkBZie0G/T//kyFAaLbIZkGD0eMXSdULhZZjdYv1FF7RFbSrXHk00QKfh2fLQWcZS9rp4Ki2rbN+LNSAWUilJvnQGb6zlxcQvMVWGBaXCJPQlfLI9o57zddv1Z2hY/IlpQFX972n4WOxYTrHvi8DyZJaz7dMbwCjoC+c5gi0hEcwMnag0ZOwWvQ87o7MEPmcqErqTVyoUUkOwA/o4ggIi2+jO+urVrcZ5S2E/ttFbSGZcGgqTG3xP/YzhcwWQ51oQKKfMSx/YCQt0oSrp1UW4O7wgPgJINnfallkNQTurWO/9oNms7TFn2PRMgAA7cJAHlbjfg3eozOOz09S419Gsn2pbqyoRd8kGU+7XkbuUWTsDkezl9mObAmn9iEyji0a3OFPS9w3+drzqDCgqaRCtE9/NwKcEjRdMYzqWYVJCg76slGX5p2eLJ1q9thCbBKrJdEIu/TMiwpsojzXcfMrXP4yyI7wNwX/EctQiQOu1ESqjNd+VIeAuzqEJh/A5YrBnvRC/jl5pJZIesrLFAPemJZ5+MvE0emH26MIZVyzSRS50ClvO694ADGEsLVVmzs8EGwtVH+nRnGfkNbk5JmDf00Q3sFKOhU5uoV5zo6WAX6jgROdsyIzw7NwRHtJ+5+JWKorYOYYJqvTNcCi26aZTz9Ij79xRa4KgDZ2Vzq4g/hfxG+duVJ9wGfSEvWL/L+RdP8CQ1xMWQvEdoGiTS+X8S159dIxoOybYo54ZFGNicMaAw9V1spOSzs+gNFc8dB5Bd204LEpTBxyvn1IPbgeBUXH+S0rYBW3VSG/8HiGRKqGw+GjSiItxXvtQ3MNY0LNqfs9iE9WjqLDXKFFbhzkAHJ7T3URnsxPJV42KateY921aFJsXlp4ZbSBi7umnyqCw3Y5aaoWiLcHcAlYKfQhB01QwvwXBW67h+FFVQAZsItvEFlJLDAbDg92gTQO5uj3oMtbJZRLgvk928SQBkGIB4FA3tOTN+G8omMN5ZE+apCDVMB16jLcHbralvq4762F/pAHJVFNb45uzXxZqtwnYh9au4pki/ajrDu4zc116DD3PoPwv6jZGxz/+IN1NzIWceB3UADHjEGaHPazNjMcD1Lof8LcEdjI299YXEWgEAkBSTUF0wKZFR86gA== X-OriginatorOrg: weidmueller.com X-MS-Exchange-CrossTenant-Network-Message-Id: 83d34cd9-6e3e-4010-23cb-08db6e5daba1 X-MS-Exchange-CrossTenant-AuthSource: PAXPR08MB6969.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 16 Jun 2023 11:34:39.9360 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: e4289438-1c5f-4c95-a51a-ee553b8b18ec X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 8/CflHTpF1CwA0etp12CO/lju6Ud7+/eng9coce0joRgh84GrD3ehLS2Tbr7XN35VR8xURTlBw41WdGCK8Bxug== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9PR08MB6444 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Malte Schmidt The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible. [1] https://uefi.org/specs/UEFI/2.10/index.html Signed-off-by: Malte Schmidt Signed-off-by: Stefan Herbrechtsmeier --- tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-) diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; }; -struct fmp_payload_header_params { - bool have_header; - uint32_t fw_version; -}; - #endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ static const char *tool_name = "mkeficapsule"; efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; -static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR"; enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'}, + {"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'}, @@ -55,21 +56,22 @@ static struct option options[] = { static void print_usage(void) { - fprintf(stderr, "Usage: %s [options] \n" + fprintf(stderr, "Usage: %s [options] [] \n" "Options:\n" - "\t-g, --guid guid for image blob type\n" - "\t-i, --index update image index\n" - "\t-I, --instance update hardware instance\n" - "\t-v, --fw-version firmware version\n" - "\t-p, --private-key private key file\n" - "\t-c, --certificate signer's certificate file\n" - "\t-m, --monotonic-count monotonic count\n" - "\t-d, --dump_sig dump signature (*.p7)\n" - "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" - "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n" - "\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n" - "\t-h, --help print a help message\n", + "\t-g, --guid comma-separated list of guids for image blob types\n" + "\t-i, --index comma-separated list of update image indices\n" + "\t-b, --image_blob comma-separated list of image blobs\n" + "\t-I, --instance comma-separated list of update hardware instances\n" + "\t-v, --fw-version comma-separated list of firmware versions\n" + "\t-p, --private-key private key file\n" + "\t-c, --certificate signer's certificate file\n" + "\t-m, --monotonic-count comma-separated list of monotonic counts\n" + "\t-d, --dump_sig dump signature (*.p7)\n" + "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" + "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n" + "\t-o, --capoemflag capsule OEM Flag, an integer between 0x0000 and 0xffff\n" + "\t-h, --help print a help message\n", tool_name); } @@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx) * @path: Path to a capsule file * @signature: Signature data * @sig_size: Size of signature data + * @index: The payload index the signature belongs to * * Signature data pointed to by @signature will be saved into - * a file whose file name is @path with ".p7" suffix. + * a file whose file name is @path with "_.p7" suffix. + * If index is negative the suffix is ".p7" (for backwards compatibility). * * Return: * * 0 - on success * * -1 - on failure */ static int dump_signature(const char *path, const uint8_t *signature, - size_t sig_size) + size_t sig_size, int index) { char *sig_path; FILE *f; @@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret; - sprintf(sig_path, "%s.p7", path); + if (index < 0) + sprintf(sig_path, "%s.p7", path); + else + sprintf(sig_path, "%s_%d.p7", path, index); + f = fopen(sig_path, "w"); if (!f) goto err; @@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /** * create_fwbin - create an uefi capsule file * @path: Path to a created capsule file - * @bin: Path to a firmware binary to encapsulate - * @guid: GUID of related FMP driver - * @index: Index number in capsule + * @bins: Paths to firmware binaries to encapsulate, an array + * @guids: GUIDs of related FMP drivers, an array + * @indices: Index numbers in capsule, an array * @instance: Instance number in capsule * @mcount: Monotonic count in authentication information + * @size: Size of the arrays * @private_file: Path to a private key file * @cert_file: Path to a certificate file - * @oemflags: Capsule OEM Flags, bits 0-15 + * @oemflags: Capsule OEM Flags, bits 0-15 * * This function actually does the job of creating an uefi capsule file. * All the arguments must be supplied. @@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int create_fwbin(const char *path, const char *bin, - const efi_guid_t *guid, unsigned long index, - unsigned long instance, - const struct fmp_payload_header_params *fmp_ph_params, - uint64_t mcount, - const char *privkey_file, const char *cert_file, - uint16_t oemflags) +static int create_fwbin(const char *path, const char **bins, + const efi_guid_t *guids, const unsigned long *indices, + const unsigned long *instances, + const unsigned long *fw_versions, const unsigned long *mcounts, + int size, const char *privkey_file, + const char *cert_file, uint16_t oemflags) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; - struct efi_firmware_management_capsule_image_header image; - struct auth_context auth_context; + struct efi_firmware_management_capsule_image_header images[size]; + struct auth_context auth_contexts[size]; FILE *f; - uint8_t *data, *new_data, *buf; - off_t bin_size; - uint64_t offset; + uint8_t *data_list[size], *new_data_list[size], *buf_list[size]; + off_t bin_sizes[size]; + uint64_t offsets[size]; int ret; - struct fmp_payload_header payload_header; + struct fmp_payload_header payload_headers[size]; #ifdef DEBUG fprintf(stderr, "For output: %s\n", path); - fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid); - fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance); + for (int i = 0; i < size; i++) { + fprintf(stderr, "\tpayload no: %d\n", i); + fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]); + fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]); + } #endif - auth_context.sig_size = 0; f = NULL; - data = NULL; - new_data = NULL; ret = -1; - /* - * read a firmware binary - */ - if (read_bin_file(bin, &data, &bin_size)) - goto err; + for (int i = 0; i < size; i++) { + auth_contexts[i].sig_size = 0; + data_list[i] = NULL; + new_data_list[i] = NULL; + } - buf = data; + for (int i = 0; i < size; i++) { + int dump_index = (size == 1) ? -1 : i; - /* insert fmp payload header right before the payload */ - if (fmp_ph_params->have_header) { - new_data = malloc(bin_size + sizeof(payload_header)); - if (!new_data) + /* + * read a firmware binary + */ + if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err; - payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE; - payload_header.header_size = sizeof(payload_header); - payload_header.fw_version = fmp_ph_params->fw_version; - payload_header.lowest_supported_version = 0; /* not used */ - memcpy(new_data, &payload_header, sizeof(payload_header)); - memcpy(new_data + sizeof(payload_header), data, bin_size); - buf = new_data; - bin_size += sizeof(payload_header); - } - - /* first, calculate signature to determine its size */ - if (privkey_file && cert_file) { - auth_context.key_file = privkey_file; - auth_context.cert_file = cert_file; - auth_context.auth.monotonic_count = mcount; - auth_context.image_data = buf; - auth_context.image_size = bin_size; - - if (create_auth_data(&auth_context)) { - fprintf(stderr, "Signing firmware image failed\n"); - goto err; + buf_list[i] = data_list[i]; + /* insert fmp payload header right before the payload */ + if (fw_versions) { + new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i])); + if (!new_data_list[i]) + goto err; + + payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE; + payload_headers[i].header_size = sizeof(payload_headers[i]); + payload_headers[i].fw_version = fw_versions[i]; + payload_headers[i].lowest_supported_version = 0; /* not used */ + memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i])); + memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i], + bin_sizes[i]); + buf_list[i] = new_data_list[i]; + bin_sizes[i] += sizeof(payload_headers[i]); } - if (dump_sig && - dump_signature(path, auth_context.sig_data, - auth_context.sig_size)) { - fprintf(stderr, "Creating signature file failed\n"); - goto err; + /* calculate signature to determine its size */ + if (privkey_file && cert_file) { + auth_contexts[i].key_file = privkey_file; + auth_contexts[i].cert_file = cert_file; + auth_contexts[i].auth.monotonic_count = mcounts[i]; + auth_contexts[i].image_data = buf_list[i]; + auth_contexts[i].image_size = bin_sizes[i]; + + if (create_auth_data(&auth_contexts[i])) { + fprintf(stderr, "Signing firmware image failed\n"); + goto err; + } + + if (dump_sig && + dump_signature(path, auth_contexts[i].sig_data, + auth_contexts[i].sig_size, dump_index)) { + fprintf(stderr, "Creating signature file failed\n"); + goto err; + } } } @@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header) - + sizeof(capsule) + sizeof(uint64_t) - + sizeof(image) - + bin_size; - if (auth_context.sig_size) - header.capsule_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; + + sizeof(capsule) + + size * sizeof(uint64_t); /* size of item_offset_list */ + for (int i = 0; i < size; i++) { + offsets[i] = header.capsule_image_size - sizeof(header); + header.capsule_image_size += sizeof(images[i]) + + bin_sizes[i]; + if (auth_contexts[i].sig_size) + header.capsule_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + } if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err; /* * firmware capsule header - * This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0; - capsule.payload_item_count = 1; + capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err; - offset = sizeof(capsule) + sizeof(uint64_t); - if (write_capsule_file(f, &offset, sizeof(offset), - "Offset to capsule image")) + if (write_capsule_file(f, &offsets, size * sizeof(uint64_t), + "Offsets to capsule images")) goto err; - /* - * firmware capsule image header - */ - image.version = 0x00000003; - memcpy(&image.update_image_type_id, guid, sizeof(*guid)); - image.update_image_index = index; - image.reserved[0] = 0; - image.reserved[1] = 0; - image.reserved[2] = 0; - image.update_image_size = bin_size; - if (auth_context.sig_size) - image.update_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; - image.update_vendor_code_size = 0; /* none */ - image.update_hardware_instance = instance; - image.image_capsule_support = 0; - if (auth_context.sig_size) - image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; - if (write_capsule_file(f, &image, sizeof(image), - "Firmware capsule image header")) - goto err; - - /* - * signature - */ - if (auth_context.sig_size) { - if (write_capsule_file(f, &auth_context.auth, - sizeof(auth_context.auth), - "Authentication header")) + for (int i = 0; i < size; i++) { + /* + * firmware capsule image header + */ + images[i].version = 0x00000003; + memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i])); + images[i].update_image_index = indices[i]; + images[i].reserved[0] = 0; + images[i].reserved[1] = 0; + images[i].reserved[2] = 0; + images[i].update_image_size = bin_sizes[i]; + if (auth_contexts[i].sig_size) + images[i].update_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + images[i].update_vendor_code_size = 0; /* none */ + images[i].update_hardware_instance = instances[i]; + images[i].image_capsule_support = 0; + if (auth_contexts[i].sig_size) + images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; + if (write_capsule_file(f, &images[i], sizeof(images[i]), + "Firmware capsule image header")) goto err; - if (write_capsule_file(f, auth_context.sig_data, - auth_context.sig_size, "Signature")) + /* + * signature + */ + if (auth_contexts[i].sig_size) { + if (write_capsule_file(f, &auth_contexts[i].auth, + sizeof(auth_contexts[i].auth), + "Authentication header")) + goto err; + + if (write_capsule_file(f, auth_contexts[i].sig_data, + auth_contexts[i].sig_size, "Signature")) + goto err; + } + + /* + * firmware binary + */ + if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; } - /* - * firmware binary - */ - if (write_capsule_file(f, buf, bin_size, "Firmware binary")) - goto err; - ret = 0; err: if (f) fclose(f); - free_sig_data(&auth_context); - free(data); - free(new_data); + for (int i = 0; i < size; i++) { + free_sig_data(&auth_contexts[i]); + free(data_list[i]); + free(new_data_list[i]); + } return ret; } @@ -652,6 +676,228 @@ err: return ret; } +/** + * count_items - count number of items in list + * @list: Pointer to a string + * @separator: Separator used to separate list items + * + * Count the number of items in a list. The list items + * are separated by a separator character inside the string. + * Trailing white spaces are not allowed except if it is the separator. + * + * Return: + * The item count. + */ +int count_items(const char *list, char separator) +{ + const char *c; + int count = 0; + + if (!*list) + return 0; + + for (c = list; *c; c++) { + if (*c == separator) + count++; + } + /* correct count if no trailing separator present */ + if (*(c - 1) != separator) + count++; + + return count; +} + +/** + * update_itemcount - update item count + * @count: The count to be updated + * @list: The item list + * @separator: List separator + * + * Initialize the count if it is uninitialized (negative value). + * Check that the list contains at least one item. + * Check if an already initialized count is consistent with the list count. + * + * Return: + * * 0 - on success + * * -1 - if a check fails + */ +int update_itemcount(int *count, const char *list, char separator) +{ + int current_count = count_items(list, separator); + + if (*count < 0) + *count = current_count; + + if (*count == 0 || + *count != current_count) + return -1; + + return 0; +} + +/** + * split_list - split list into elements + * @elements: Pointer to string array + * @size: The array size + * @list: The item list + * @separator: List separator + * + * Split a comma-separated list into its elements. + * + * Return: + * * 0 - on success + * * -1 - on failure + */ +int split_list(char **elements, int size, char *list, char separator) +{ + const char separator_str[] = {separator, '\0'}; + char *end; + + for (int i = 0; i < size; i++) { + elements[i] = strsep(&list, separator_str); + if (!elements[i]) + return -1; + } + + end = strsep(&list, separator_str); /* NULL or empty string expected */ + if (end && *end) + return -1; + + return 0; +} + +/** + * alloc_array - allocate memory for array + * @count: The number of elements + * @obj_size: The size of a single element + * @name: The name of the array + * + * This is a wrapper for malloc which prints an error + * message on failure. + * + * Return: + * * Pointer to the allocated memory on success + * * NULL on failure + */ +void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{ + void *array; + + array = malloc(count * obj_size); + if (!array) + fprintf(stderr, "Could not allocate memory for %s\n", name); + + return array; +} + +/** + * init_guids - populate guid array + * @elements: String array of elements to be converted + * @size: The array size + * @name: The name of the array + * + * Allocate and populate an array of guid structs. The list contains the UUIDs + * to convert and store in the array. Upon failure an error message is + * printed. + * + * Return: + * * The initialized GUID array on success + * * NULL on failure + */ +efi_guid_t *init_guids(const char **elements, unsigned int size, + const char *name) +{ + efi_guid_t *guids; + + guids = alloc_array(size, sizeof(efi_guid_t), name); + if (!guids) + return NULL; + + for (int i = 0; i < size; i++) { + if (uuid_parse(elements[i], (unsigned char *)(guids + i))) { + fprintf(stderr, "Wrong %s format\n", name); + free(guids); + return NULL; + } + convert_uuid_to_guid((unsigned char *)(guids + i)); + } + + return guids; +} + +/** + * init_uls - populate unsigned long array + * @elements: String array of elements to be converted + * @size: The array size + * @name: The name of the array + * + * Allocate and populate an array of unsgined longs. Upon failure an + * error message is printed. + * + * Return: + * * The initialized array on success + * * NULL on failure + */ +unsigned long *init_uls(const char **elements, unsigned int size, + const char *name) +{ + unsigned long *array; + + array = alloc_array(size, sizeof(unsigned long), name); + if (!array) + return NULL; + for (int i = 0; i < size; i++) + array[i] = strtoul(elements[i], NULL, 0); + + return array; +} + +/** + * init_list - parse list and allocate elements + * @listcount: The list count to be checked and updated + * @list: The list to be parsed + * @separator: The list separator + * @name: The name of the list + * @multiple_times: List encountered multiple times + * + * Routine for command line argument lists. + * Parse the string list and count the list elements. + * Initialize the listcount if it is uninitialized (negative value). + * Check that the list contains at least one item. + * Check if an already initialized count is consistent with the list count. + * Allocate the string array and populate it with the list elements. + * The array should be freed in the calling function. + * Upon failure an error message is printed and the program exits. + * + * Return: + * * The initialized array on success + * * NULL on failure + */ +char **init_list(int *listcount, char *list, char separator, + bool multiple_times, char *name) +{ + char **elements; + + if (multiple_times) { + fprintf(stderr, "%s specified multiple times\n", name); + return NULL; + } + if (update_itemcount(listcount, list, separator)) { + fprintf(stderr, "List count not consistent with previous or list not provided\n"); + return NULL; + } + elements = alloc_array(*listcount, sizeof(char *), name); + if (!elements) + return NULL; + if (split_list(elements, *listcount, list, separator)) { + fprintf(stderr, "Could not parse %s list\n", name); + free(elements); + return NULL; + } + + return elements; +} + /** * main - main entry function of mkeficapsule * @argc: Number of arguments @@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) { - efi_guid_t *guid; - unsigned char uuid_buf[16]; - unsigned long index, instance; - uint64_t mcount; + const char separator = ','; + const efi_guid_t *guids; /* an array */ + const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags; + const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file; - int c, idx; - struct fmp_payload_header_params fmp_ph_params = { 0 }; + int listcount, c, idx; - guid = NULL; - index = 0; - instance = 0; - mcount = 0; + guids = NULL; + indices = NULL; + instances = NULL; + mcounts = NULL; + oemflags = 0; + blob_paths = NULL; privkey_file = NULL; cert_file = NULL; + elements = NULL; + listcount = -1; + fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB; - oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) @@ -691,27 +940,62 @@ int main(int argc, char **argv) switch (c) { case 'g': - if (guid) { - fprintf(stderr, - "Image type already specified\n"); + elements = (const char **)init_list(&listcount, optarg, separator, !!guids, + "GUID"); + if (!elements) exit(EXIT_FAILURE); - } - if (uuid_parse(optarg, uuid_buf)) { - fprintf(stderr, "Wrong guid format\n"); + + guids = init_guids(elements, listcount, "GUID"); + if (!guids) exit(EXIT_FAILURE); - } - convert_uuid_to_guid(uuid_buf); - guid = (efi_guid_t *)uuid_buf; + + free(elements); + elements = NULL; break; case 'i': - index = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator, + !!indices, "index"); + if (!elements) + exit(EXIT_FAILURE); + + indices = init_uls(elements, listcount, "index"); + if (!indices) + exit(EXIT_FAILURE); + + free(elements); + elements = NULL; + break; + case 'b': + blob_paths = (const char **)init_list(&listcount, optarg, separator, + !!blob_paths, "blob path"); + if (!blob_paths) + exit(EXIT_FAILURE); break; case 'I': - instance = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator, + !!instances, "instance"); + if (!elements) + exit(EXIT_FAILURE); + + instances = init_uls(elements, listcount, "instance"); + if (!instances) + exit(EXIT_FAILURE); + + free(elements); + elements = NULL; break; case 'v': - fmp_ph_params.fw_version = strtoul(optarg, NULL, 0); - fmp_ph_params.have_header = true; + elements = (const char **)init_list(&listcount, optarg, separator, + !!fw_versions, "firmware version"); + if (!elements) + exit(EXIT_FAILURE); + + fw_versions = init_uls(elements, listcount, "firmware version"); + if (!fw_versions) + exit(EXIT_FAILURE); + + free(elements); + elements = NULL; break; case 'p': if (privkey_file) { @@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm': - mcount = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator, + !!mcounts, "monotonic count"); + if (!elements) + exit(EXIT_FAILURE); + + mcounts = init_uls(elements, listcount, "monotonic count"); + if (!mcounts) + exit(EXIT_FAILURE); + + free(elements); + elements = NULL; break; case 'd': dump_sig = 1; @@ -767,26 +1061,46 @@ int main(int argc, char **argv) /* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB && - ((argc != optind + 2) || !guid || - ((privkey_file && !cert_file) || - (!privkey_file && cert_file)))) || + (!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids || + ((privkey_file && !cert_file) || + (!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB && - ((argc != optind + 1) || - ((capsule_type == CAPSULE_ACCEPT) && !guid) || - ((capsule_type == CAPSULE_REVERT) && guid)))) { + ((argc != optind + 1) || + ((capsule_type == CAPSULE_ACCEPT) && !guids) || + ((capsule_type == CAPSULE_ACCEPT) && listcount != 1) || + ((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); } + /* populate blob_paths if image blob was provided as positional argument */ + if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) { + blob_paths = malloc(sizeof(char *)); + if (!blob_paths) { + fprintf(stderr, "Could not allocate memory for blob paths\n"); + exit(EXIT_FAILURE); + } + *blob_paths = argv[argc - 2]; + } + + /* populate arrays with zeros if they are not provided */ + if (!indices) + indices = calloc(listcount, sizeof(unsigned long)); + if (!instances) + instances = calloc(listcount, sizeof(unsigned long)); + if (!mcounts) + mcounts = calloc(listcount, sizeof(uint64_t)); + if (capsule_type != CAPSULE_NORMAL_BLOB) { - if (create_empty_capsule(argv[argc - 1], guid, + if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); } - } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, - index, instance, &fmp_ph_params, mcount, privkey_file, - cert_file, (uint16_t)oemflags) < 0) { + } else if (create_fwbin(argv[argc - 1], blob_paths, guids, + indices, instances, fw_versions, + mcounts, listcount, privkey_file, + cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }