From patchwork Sun Jul 9 13:33:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sughosh Ganu X-Patchwork-Id: 700805 Delivered-To: patch@linaro.org Received: by 2002:adf:fcc5:0:0:0:0:0 with SMTP id f5csp4732525wrs; Sun, 9 Jul 2023 06:34:31 -0700 (PDT) X-Google-Smtp-Source: APBJJlEsqPoOTHi9IHAKVR3/fkzEsDFx4ZYaLBOIHbavjbeBMuvu7SqJSu9bd8AJ6OQoJLagboz+ X-Received: by 2002:a05:6602:148f:b0:786:a615:6281 with SMTP id a15-20020a056602148f00b00786a6156281mr10546776iow.3.1688909671276; Sun, 09 Jul 2023 06:34:31 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1688909671; cv=none; d=google.com; s=arc-20160816; b=S7gNP8ILFqO2gI8dIZQdofLz6qzD7I3kiFDeGVOEIdHQ4ERoz5M5zhHD1foSuoT1Nh IIuU5jEpYCHfINK98KqDYuxvbTWtMIriA6+S+MLdKoZKqT3L0qYbtYKzCvOx/yNURKQp rOCrDJSIC46cwJbvXDDKKMgBv4WDmrgVUDQ9bIhUxN3lpJHD4C/ubzis3g7Zd0jNMfMF UvRxStK0cmU36khGtlZFoCml/2CA+L76tMYk/bndGZP8UDtgFF6v7gK21zhyrP0FWUgp rm+w5be7lwChQEN6+BdWtvaq09U2f/MT4vjHLt5PZl31Tr0iX0GIoxzoQVDS0MCz2XhT TXOw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from; bh=Vu+bhI17m0+7dreqnFAAgMnpUIkFEvd+RIA140nRIik=; fh=5yufCDEpV5fWA8r3jJ0mHshq3zHArlCzbvaufLK2jtI=; b=i8T9lJTFPQaJdM82kRt/6ZrXgc3i7aHaDxJBjF1bt+RIguVxsVWvPwU3sh6EYlWBmP Vd0n32q+uOELEYy0JMjHaJSI3Y6Yaoz/nFfnMGxaDKGC/u0WsCimZx9JGyBHc4UdKhPW 53u14bCCFyoAAozYIPqKQRkg0/ceu7UZX4D10Km/DxvQyIGuJvKu21kTOgz66I8K/0SF 7q2Ab6bKRsHyh/uXsMLtpc1DJez2XDl1p81rQYWVOg/2s3n9vgEQzKU9LG1X7fUbzWKQ rCG1tHrFENiI2rtcQOhUYMPTIE0nw5kEuu8RMFkf7AzZ8iI5zYp3oQurD4gXZhDqCguN a3NA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 2a01:238:438b:c500:173d:9f52:ddab:ee01 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from phobos.denx.de (phobos.denx.de. [2a01:238:438b:c500:173d:9f52:ddab:ee01]) by mx.google.com with ESMTPS id m36-20020a056638272400b004266a4ce819si3618003jav.17.2023.07.09.06.34.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 09 Jul 2023 06:34:31 -0700 (PDT) Received-SPF: pass (google.com: domain of u-boot-bounces@lists.denx.de designates 2a01:238:438b:c500:173d:9f52:ddab:ee01 as permitted sender) client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; Authentication-Results: mx.google.com; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 2a01:238:438b:c500:173d:9f52:ddab:ee01 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 954B3846D9; Sun, 9 Jul 2023 15:34:00 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by phobos.denx.de (Postfix, from userid 109) id 6C8E6865D8; Sun, 9 Jul 2023 15:33:59 +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=-1.2 required=5.0 tests=BAYES_00,SPF_HELO_NONE, SPF_SOFTFAIL,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by phobos.denx.de (Postfix) with ESMTP id EACB185990 for ; Sun, 9 Jul 2023 15:33:55 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=fail smtp.mailfrom=sughosh.ganu@linaro.org Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 86A9AD75; Sun, 9 Jul 2023 06:34:37 -0700 (PDT) Received: from a076522.blr.arm.com (a076522.blr.arm.com [10.162.46.7]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id D08D43F762; Sun, 9 Jul 2023 06:33:52 -0700 (PDT) From: Sughosh Ganu To: u-boot@lists.denx.de Cc: Heinrich Schuchardt , Ilias Apalodimas , Simon Glass , Takahiro Akashi , Malte Schmidt , Tom Rini , Sughosh Ganu Subject: [PATCH v3 04/11] tools: mkeficapsule: Add support for parsing capsule params from config file Date: Sun, 9 Jul 2023 19:03:19 +0530 Message-Id: <20230709133326.1015483-5-sughosh.ganu@linaro.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230709133326.1015483-1-sughosh.ganu@linaro.org> References: <20230709133326.1015483-1-sughosh.ganu@linaro.org> MIME-Version: 1.0 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 Add support for specifying the parameters needed for capsule generation through a config file, instead of passing them through command-line. Parameters for more than a single capsule file can be specified, resulting in generation of multiple capsules through a single invocation of the command. This path is to be used for generating capsules through a make target, with the parameters being parsed from the config file. Signed-off-by: Sughosh Ganu --- Changes since V2: * Add a Kconfig boolean symbol CONFIG_EFI_USE_CAPSULE_CFG_FILE which can be used to generate capsules through config file or parameters. tools/Kconfig | 16 ++ tools/Makefile | 1 + tools/eficapsule.h | 110 ++++++++++++ tools/mkeficapsule.c | 84 +++++---- tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++ 5 files changed, 526 insertions(+), 30 deletions(-) create mode 100644 tools/mkeficapsule_parse.c diff --git a/tools/Kconfig b/tools/Kconfig index 539708f277..9b744aba31 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -98,6 +98,22 @@ config TOOLS_MKEFICAPSULE optionally sign that file. If you want to enable UEFI capsule update feature on your target, you certainly need this. +config EFI_CAPSULE_CFG_FILE + string "Path to the EFI Capsule Config File" + default "" + help + Path to the EFI capsule config file which provides the + parameters needed to build capsule(s). Parameters can be + provided for multiple payloads resulting in corresponding + capsule images being generated. + +config EFI_USE_CAPSULE_CFG_FILE + bool "Use the config file for generating capsules" + help + Boolean option used to specify if the EFI capsules are to + be generated through parameters specified via the config + file or through command line. + menuconfig FSPI_CONF_HEADER bool "FlexSPI Header Configuration" help diff --git a/tools/Makefile b/tools/Makefile index d793cf3bec..ef366f3d61 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \ HOSTLDLIBS_mkeficapsule += \ $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid") hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o # We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 072a4b5598..42e66c6d6a 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -52,6 +52,38 @@ typedef struct { /* flags */ #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000 +enum capsule_type { + CAPSULE_NORMAL_BLOB = 0, + CAPSULE_ACCEPT, + CAPSULE_REVERT, +}; + +/** + * struct efi_capsule_params - Capsule parameters + * @image_guid: Guid value of the payload input image + * @image_index: Image index value + * @hardware_instance: Hardware instance to be used for the image + * @monotonic_count: Monotonic count value to be used for signed capsule + * @privkey_file: Path to private key used in capsule signing + * @cert_file: Path to public key certificate used in capsule signing + * @input_file: Path to payload input image + * @capsule_file: Path to the output capsule file + * @oemflags: Oemflags to be populated in the capsule header + * @capsule: Capsule Type, normal or accept or revert + */ +struct efi_capsule_params { + efi_guid_t *image_guid; + unsigned long image_index; + unsigned long hardware_instance; + uint64_t monotonic_count; + char *privkey_file; + char *cert_file; + char *input_file; + char *capsule_file; + unsigned long oemflags; + enum capsule_type capsule; +}; + struct efi_capsule_header { efi_guid_t capsule_guid; uint32_t header_size; @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication { struct win_certificate_uefi_guid auth_info; } __packed; +/** + * capsule_with_cfg_file() - Generate capsule from config file + * @cfg_file: Path to the config file + * + * Parse the capsule parameters from the config file and use the + * parameters for generating one or more capsules. + * + * Return: None + * + */ +void capsule_with_cfg_file(const char *cfg_file); + +/** + * convert_uuid_to_guid() - convert UUID to GUID + * @buf: UUID binary + * + * UUID and GUID have the same data structure, but their binary + * formats are different due to the endianness. See lib/uuid.c. + * Since uuid_parse() can handle only UUID, this function must + * be called to get correct data for GUID when parsing a string. + * + * The correct data will be returned in @buf. + */ +void convert_uuid_to_guid(unsigned char *buf); + +/** + * create_empty_capsule() - Generate an empty capsule + * @path: Path to the empty capsule file to be generated + * @guid: Guid value of the image for which empty capsule is generated + * @fw_accept: Flag to specify whether to generate accept or revert capsule + * + * Generate an empty capsule, either an accept or a revert capsule to be + * used to flag acceptance or rejection of an earlier executed firmware + * update operation. Being used in the FWU Multi Bank firmware update + * feature. + * + * Return: 0 if OK, -ve on error + * + */ +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept); + +/** + * 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 + * @instance: Instance number in capsule + * @mcount: Monotonic count in authentication information + * @private_file: Path to a private key file + * @cert_file: Path to a certificate file + * @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. + * If either @private_file ror @cert_file is NULL, the capsule file + * won't be signed. + * + * Return: + * * 0 - on success + * * -1 - on failure + */ +int create_fwbin(char *path, char *bin, efi_guid_t *guid, + unsigned long index, unsigned long instance, + uint64_t mcount, char *privkey_file, char *cert_file, + uint16_t oemflags); + +/** + * print_usage() - Print the command usage string + * + * Prints the standard command usage string. Called in the case + * of incorrect parameters being passed to the tool. + * + * Return: None + * + */ +void print_usage(void); + #endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b71537beee..522dc4b474 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,13 +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"; - -enum { - CAPSULE_NORMAL_BLOB = 0, - CAPSULE_ACCEPT, - CAPSULE_REVERT, -} capsule_type; +static const char *opts_short = "g:i:I:v:p:c:m:o:f:dhAR"; static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -48,11 +42,21 @@ static struct option options[] = { {"fw-accept", no_argument, NULL, 'A'}, {"fw-revert", no_argument, NULL, 'R'}, {"capoemflag", required_argument, NULL, 'o'}, + {"cfg-file", required_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, }; -static void print_usage(void) +/** + * print_usage() - Print the command usage string + * + * Prints the standard command usage string. Called in the case + * of incorrect parameters being passed to the tool. + * + * Return: None + * + */ +void print_usage(void) { fprintf(stderr, "Usage: %s [options] \n" "Options:\n" @@ -67,6 +71,7 @@ static void print_usage(void) "\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-f, --cfg-file config file with capsule parameters\n" "\t-h, --help print a help message\n", tool_name); } @@ -400,10 +405,10 @@ static void free_sig_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int create_fwbin(char *path, char *bin, efi_guid_t *guid, - unsigned long index, unsigned long instance, - uint64_t mcount, char *privkey_file, char *cert_file, - uint16_t oemflags) +int create_fwbin(char *path, char *bin, efi_guid_t *guid, + unsigned long index, unsigned long instance, + uint64_t mcount, char *privkey_file, char *cert_file, + uint16_t oemflags) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; @@ -580,7 +585,21 @@ void convert_uuid_to_guid(unsigned char *buf) buf[7] = c; } -static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept) +/** + * create_empty_capsule() - Generate an empty capsule + * @path: Path to the empty capsule file to be generated + * @guid: Guid value of the image for which empty capsule is generated + * @fw_accept: Flag to specify whether to generate accept or revert capsule + * + * Generate an empty capsule, either an accept or a revert capsule to be + * used to flag acceptance or rejection of an earlier executed firmware + * update operation. Being used in the FWU Multi Bank firmware update + * feature. + * + * Return: 0 if OK, -ve on error + * + */ +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept) { struct efi_capsule_header header = { 0 }; FILE *f = NULL; @@ -643,6 +662,8 @@ int main(int argc, char **argv) uint64_t mcount; unsigned long oemflags; char *privkey_file, *cert_file; + char *cfg_file; + enum capsule_type capsule; int c, idx; guid = NULL; @@ -651,8 +672,9 @@ int main(int argc, char **argv) mcount = 0; privkey_file = NULL; cert_file = NULL; + cfg_file = NULL; dump_sig = 0; - capsule_type = CAPSULE_NORMAL_BLOB; + capsule = CAPSULE_NORMAL_BLOB; oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); @@ -702,20 +724,20 @@ int main(int argc, char **argv) dump_sig = 1; break; case 'A': - if (capsule_type) { + if (capsule) { fprintf(stderr, "Select either of Accept or Revert capsule generation\n"); exit(1); } - capsule_type = CAPSULE_ACCEPT; + capsule = CAPSULE_ACCEPT; break; case 'R': - if (capsule_type) { + if (capsule) { fprintf(stderr, "Select either of Accept or Revert capsule generation\n"); exit(1); } - capsule_type = CAPSULE_REVERT; + capsule = CAPSULE_REVERT; break; case 'o': oemflags = strtoul(optarg, NULL, 0); @@ -725,6 +747,10 @@ int main(int argc, char **argv) exit(1); } break; + case 'f': + cfg_file = optarg; + capsule_with_cfg_file(cfg_file); + exit(EXIT_SUCCESS); default: print_usage(); exit(EXIT_SUCCESS); @@ -732,21 +758,21 @@ 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)))) || - (capsule_type != CAPSULE_NORMAL_BLOB && - ((argc != optind + 1) || - ((capsule_type == CAPSULE_ACCEPT) && !guid) || - ((capsule_type == CAPSULE_REVERT) && guid)))) { + if ((capsule == CAPSULE_NORMAL_BLOB && + ((argc != optind + 2) || !guid || + ((privkey_file && !cert_file) || + (!privkey_file && cert_file)))) || + (capsule != CAPSULE_NORMAL_BLOB && + ((argc != optind + 1) || + (capsule == CAPSULE_ACCEPT && !guid) || + (capsule == CAPSULE_REVERT && guid)))) { print_usage(); exit(EXIT_FAILURE); } - if (capsule_type != CAPSULE_NORMAL_BLOB) { + if (capsule != CAPSULE_NORMAL_BLOB) { if (create_empty_capsule(argv[argc - 1], guid, - capsule_type == CAPSULE_ACCEPT) < 0) { + capsule == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); } @@ -756,6 +782,4 @@ int main(int argc, char **argv) fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); } - - exit(EXIT_SUCCESS); } diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c new file mode 100644 index 0000000000..ef4f3f6705 --- /dev/null +++ b/tools/mkeficapsule_parse.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 Linaro Limited + */ + +/* + * The code in this file adds parsing ability to the mkeficapsule + * tool. This allows specifying parameters needed to build the capsule + * through the config file instead of specifying them on the command-line. + * Parameters can be specified for more than one payload, generating the + * corresponding capsule files. + * + * The parameters are specified in a "key:value" pair. All the parameters + * that are currently supported by the mkeficapsule tool can be specified + * in the config file. + * + * The example below shows four payloads. The first payload is an example + * of generating a signed capsule. The second payload is an example of + * generating an unsigned capsule. The third payload is an accept empty + * capsule, while the fourth payload is the revert empty capsule, used + * for the multi-bank firmware update feature. + * + * This functionality can be easily extended to generate a single capsule + * comprising multiple payloads. + + { + image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660 + hardware-instance: 0 + monotonic-count: 1 + payload: u-boot.bin + image-index: 1 + private-key: /path/to/priv/key + pub-key-cert: /path/to/pub/key + capsule: u-boot.capsule + } + { + image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e + hardware-instance: 0 + payload: u-boot.itb + image-index: 2 + oemflags: 0x8000 + capsule: fit.capsule + } + { + capsule-type: accept + image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e + capsule: accept.capsule + } + { + capsule-type: revert + capsule: revert.capsule + } +*/ + +#include +#include +#include +#include +#include + +#include + +#include "eficapsule.h" + +#define PARAMS_START "{" +#define PARAMS_END "}" + +#define PSTART 2 +#define PEND 3 + +#define MALLOC_FAIL_STR "Unable to allocate memory\n" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +const char *capsule_params[] = { + "image-guid", "image-index", "private-key", + "pub-key-cert", "payload", "capsule", + "hardware-instance", "monotonic-count", + "capsule-type", "oemflags" }; + +static unsigned char params_start; +static unsigned char params_end; + +static void print_and_exit(const char *str) +{ + fprintf(stderr, "%s", str); + exit(EXIT_FAILURE); +} + +static int param_delim_checks(char *line, unsigned char *token) +{ + if (!strcmp(line, PARAMS_START)) { + if (params_start || !params_end) { + fprintf(stderr, "Earlier params processing still in progress. "); + fprintf(stderr, "Can't start processing a new params.\n"); + exit(EXIT_FAILURE); + } else { + params_start = 1; + params_end = 0; + *token = PSTART; + return 1; + } + } else if (!strcmp(line, PARAMS_END)) { + if (!params_start) { + fprintf(stderr, "Cannot put end braces without start braces. "); + fprintf(stderr, "Please check the documentation for reference config file syntax\n"); + exit(EXIT_FAILURE); + } else { + params_start = 0; + params_end = 1; + *token = PEND; + return 1; + } + } else if (!params_start) { + fprintf(stderr, "Params should be passed within braces. "); + fprintf(stderr, "Please check the documentation for reference config file syntax\n"); + exit(EXIT_FAILURE); + } + + return 0; +} + +static void add_guid(efi_guid_t **guid_param, char *guid) +{ + unsigned char uuid_buf[16]; + + *guid_param = malloc(sizeof(efi_guid_t)); + if (!*guid_param) + print_and_exit(MALLOC_FAIL_STR); + + if (uuid_parse(guid, uuid_buf)) + print_and_exit("Wrong guid format\n"); + + convert_uuid_to_guid(uuid_buf); + memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t)); +} + +static void add_string(char **dst, char *val) +{ + *dst = strdup(val); + if (!*dst) + print_and_exit(MALLOC_FAIL_STR); +} + +static void match_and_populate_param(char *key, char *val, + struct efi_capsule_params *param) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(capsule_params); i++) { + if (!strcmp(key, capsule_params[i])) { + switch (i) { + case 0: + add_guid(¶m->image_guid, val); + return; + case 1: + param->image_index = strtoul(val, NULL, 0); + if (param->image_index == ULONG_MAX) + print_and_exit("Enter a valid value of index bewtween 1-255"); + return; + case 2: + add_string(¶m->privkey_file, val); + return; + case 3: + add_string(¶m->cert_file, val); + return; + case 4: + add_string(¶m->input_file, val); + return; + case 5: + add_string(¶m->capsule_file, val); + return; + case 6: + param->hardware_instance = strtoul(val, NULL, 0); + if (param->hardware_instance == ULONG_MAX) + print_and_exit("Enter a valid hardware instance value"); + return; + case 7: + param->monotonic_count = strtoull(val, NULL, 0); + if (param->monotonic_count == ULLONG_MAX) + print_and_exit("Enter a valid monotonic count value"); + return; + case 8: + if (!strcmp(val, "normal")) + param->capsule = CAPSULE_NORMAL_BLOB; + else if (!strcmp(val, "accept")) + param->capsule = CAPSULE_ACCEPT; + else if (!strcmp(val, "revert")) + param->capsule = CAPSULE_REVERT; + else + print_and_exit("Invalid type of capsule"); + + return; + case 9: + param->oemflags = strtoul(val, NULL, 0); + if (param->oemflags > 0xffff) + print_and_exit("OemFlags must be between 0x0 and 0xffff\n"); + return; + } + } + } + + fprintf(stderr, "Undefined param %s specified. ", key); + fprintf(stderr, "Please check the documentation for reference config file syntax\n"); + exit(EXIT_FAILURE); +} + +static int get_capsule_params(char *line, struct efi_capsule_params *params) +{ + char *key = NULL; + char *val = NULL; + unsigned char token; + + if (param_delim_checks(line, &token)) + return token; + + key = strtok(line, ":"); + if (key) + val = strtok(NULL, "\0"); + else + print_and_exit("Expect the params in a key:value pair\n"); + + match_and_populate_param(key, val, params); + + return 0; +} + +static char *skip_whitespace(char *line) +{ + char *ptr, *newline; + + ptr = malloc(strlen(line) + 1); + if (!ptr) + print_and_exit(MALLOC_FAIL_STR); + + for (newline = ptr; *line; line++) + if (!isblank(*line)) + *ptr++ = *line; + *ptr = '\0'; + return newline; +} + +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params) +{ + char *line = NULL; + char *newline; + size_t n = 0; + ssize_t len; + + while ((len = getline(&line, &n, fp)) != -1) { + if (len == 1 && line[len - 1] == '\n') + continue; + + line[len - 1] = '\0'; + + newline = skip_whitespace(line); + + if (newline[0] == '#') + continue; + + if (get_capsule_params(newline, params) == PEND) + return 0; + } + + if (errno == EINVAL || errno == ENOMEM) { + fprintf(stderr, "getline() returned an error %s reading the line\n", + strerror(errno)); + exit(EXIT_FAILURE); + } else if (params_start == 1 || params_end == 0) { + fprintf(stderr, "Params should be passed within braces. "); + fprintf(stderr, "Please check the documentation for reference config file syntax\n"); + exit(EXIT_FAILURE); + } else { + return -1; + } +} + +static void params_dependency_check(struct efi_capsule_params *params) +{ + /* check necessary parameters */ + if ((params->capsule == CAPSULE_NORMAL_BLOB && + ((!params->input_file || !params->capsule_file || + !params->image_guid) || + ((params->privkey_file && !params->cert_file) || + (!params->privkey_file && params->cert_file)))) || + (params->capsule != CAPSULE_NORMAL_BLOB && + (!params->capsule_file || + (params->capsule == CAPSULE_ACCEPT && !params->image_guid) || + (params->capsule == CAPSULE_REVERT && params->image_guid)))) { + print_usage(); + exit(EXIT_FAILURE); + } +} + +static void generate_capsule(struct efi_capsule_params *params) +{ + if (params->capsule != CAPSULE_NORMAL_BLOB) { + if (create_empty_capsule(params->capsule_file, + params->image_guid, + params->capsule == + CAPSULE_ACCEPT) < 0) + print_and_exit("Creating empty capsule failed\n"); + } else if (create_fwbin(params->capsule_file, params->input_file, + params->image_guid, params->image_index, + params->hardware_instance, + params->monotonic_count, + params->privkey_file, + params->cert_file, + (uint16_t)params->oemflags) < 0) { + print_and_exit("Creating firmware capsule failed\n"); + } +} + +/** + * capsule_with_cfg_file() - Generate capsule from config file + * @cfg_file: Path to the config file + * + * Parse the capsule parameters from the config file and use the + * parameters for generating one or more capsules. + * + * Return: None + * + */ +void capsule_with_cfg_file(const char *cfg_file) +{ + FILE *fp; + struct efi_capsule_params params = { 0 }; + + fp = fopen(cfg_file, "r"); + if (!fp) { + fprintf(stderr, "Unable to open the capsule config file %s\n", + cfg_file); + exit(EXIT_FAILURE); + } + + params_start = 0; + params_end = 1; + + while (parse_capsule_payload_params(fp, ¶ms) != -1) { + params_dependency_check(¶ms); + generate_capsule(¶ms); + + memset(¶ms, 0, sizeof(struct efi_capsule_params)); + } +}