diff mbox series

[v3,06/11] binman: capsule: Add support for generating capsules

Message ID 20230709133326.1015483-7-sughosh.ganu@linaro.org
State New
Headers show
Series Integrate EFI capsule tasks into u-boot's build flow | expand

Commit Message

Sughosh Ganu July 9, 2023, 1:33 p.m. UTC
Add support in binman for generating capsules. The capsule parameters
can be specified either through a config file or through the capsule
binman entry.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
Changes since V2:
* New patch which generates capsules through binman replacing the
  earlier make target.

 tools/binman/btool/mkeficapsule.py |  91 +++++++++++++++++++++++++
 tools/binman/entries.rst           |  27 ++++++++
 tools/binman/etype/capsule.py      | 102 +++++++++++++++++++++++++++++
 3 files changed, 220 insertions(+)
 create mode 100644 tools/binman/btool/mkeficapsule.py
 create mode 100644 tools/binman/etype/capsule.py

Comments

Simon Glass July 10, 2023, 9:38 p.m. UTC | #1
Hi Sughosh,

On Sun, 9 Jul 2023 at 07:34, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> Add support in binman for generating capsules. The capsule parameters
> can be specified either through a config file or through the capsule
> binman entry.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
> Changes since V2:
> * New patch which generates capsules through binman replacing the
>   earlier make target.
>
>  tools/binman/btool/mkeficapsule.py |  91 +++++++++++++++++++++++++
>  tools/binman/entries.rst           |  27 ++++++++
>  tools/binman/etype/capsule.py      | 102 +++++++++++++++++++++++++++++
>  3 files changed, 220 insertions(+)
>  create mode 100644 tools/binman/btool/mkeficapsule.py
>  create mode 100644 tools/binman/etype/capsule.py

Please do check test coverage (binman test -T). You are missing quite
a lot in two two files you have added.

>
> diff --git a/tools/binman/btool/mkeficapsule.py b/tools/binman/btool/mkeficapsule.py
> new file mode 100644
> index 0000000000..9f656c12cf
> --- /dev/null
> +++ b/tools/binman/btool/mkeficapsule.py
> @@ -0,0 +1,91 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +# Copyright 2023 Linaro Limited
> +#
> +"""Bintool implementation for mkeficapsule tool
> +
> +mkeficapsule is a tool used for generating EFI capsules.
> +
> +The following are the command-line options to be provided
> +to the tool
> +Usage: mkeficapsule [options] <image blob> <output file>
> +Options:
> +       -g, --guid <guid string>    guid for image blob type
> +       -i, --index <index>         update image index
> +       -I, --instance <instance>   update hardware instance
> +       -p, --private-key <privkey file>  private key file
> +       -c, --certificate <cert file>     signer's certificate file
> +       -m, --monotonic-count <count>     monotonic count
> +       -d, --dump_sig              dump signature (*.p7)
> +       -A, --fw-accept  firmware accept capsule, requires GUID, no image blob
> +       -R, --fw-revert  firmware revert capsule, takes no GUID, no image blob
> +       -o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff
> +       -f, --cfg-file <config file> config file with capsule parameters
> +       -h, --help                  print a help message
> +
> +"""
> +
> +from binman import bintool
> +
> +class Bintoolmkeficapsule(bintool.Bintool):
> +    """Handles the 'mkeficapsule' tool
> +
> +    This bintool is used for generating the EFI capsules. The
> +    capsule generation parameters can either be specified through
> +    command-line, or through a config file.
> +
> +    """
> +    def __init__(self, name):
> +        super().__init__(name, 'mkeficapsule tool for generating capsules')
> +
> +    def capsule_cfg_file(self, cfg_file):

"""Function comment

Args:
   cfg_file (str): ...
"""

Please fix throughout

> +
> +        args = [
> +            f'--cfg-file={cfg_file}'
> +        ]
> +        self.run_cmd(*args)
> +
> +    def cmdline_capsule(self, image_index, image_guid, hardware_instance,
> +                        payload, output_fname):
> +
> +        args = [
> +            f'--index={image_index}',
> +            f'--guid={image_guid}',
> +            f'--instance={hardware_instance}',
> +            payload,
> +            output_fname
> +        ]
> +        self.run_cmd(*args)
> +
> +    def cmdline_auth_capsule(self, image_index, image_guid, hardware_instance,
> +                             monotonic_count, priv_key, pub_key,
> +                             payload, output_fname):
> +
> +        args = [
> +            f'--index={image_index}',
> +            f'--guid={image_guid}',
> +            f'--instance={hardware_instance}',
> +            f'--monotonic-count={monotonic_count}',
> +            f'--private-key={priv_key}',
> +            f'--certificate={pub_key}',
> +            payload,
> +            output_fname
> +        ]
> +        self.run_cmd(*args)
> +
> +    def fetch(self, method):
> +        """Fetch handler for mkeficapsule
> +
> +        This builds the tool from source
> +
> +        Returns:
> +            tuple:
> +                str: Filename of fetched file to copy to a suitable directory
> +                str: Name of temp directory to remove, or None
> +        """
> +        if method != bintool.FETCH_BUILD:
> +            return None
> +        result = self.build_from_git(
> +            'https://source.denx.de/u-boot/u-boot.git',
> +            'tools',
> +            'tools/mkeficapsule')
> +        return result

Can we just install u-boot-tools? But if not, this is OK.

But it does not actually work:

$ binman tool -f mkeficapsule
Fetch: mkeficapsule
- trying method: binary download
- trying method: build from source
- clone git repo 'https://source.denx.de/u-boot/u-boot.git' to
'/tmp/binmanf.chfbsxc0'
- build target 'tools'
Exception: Error 2 running 'make -C /tmp/binmanf.chfbsxc0 -j 64 tools': ***
*** Configuration file ".config" not found!
***
*** Please run some configurator (e.g. "make oldconfig" or
*** "make menuconfig" or "make xconfig").
***
make[2]: *** [scripts/kconfig/Makefile:75: syncconfig] Error 1
make[1]: *** [Makefile:576: syncconfig] Error 2
make: *** No rule to make target 'include/config/auto.conf', needed by
'include/config/uboot.release'.  Stop.

- failed to fetch with all methods

I think you need a make tools_defconfig in there.

> diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
> index b71af801fd..9a263e8691 100644
> --- a/tools/binman/entries.rst
> +++ b/tools/binman/entries.rst
> @@ -283,6 +283,33 @@ entry; similarly for SPL.
>
>
>
> +.. _etype_capsule:
> +
> +Entry: capsule: Entry for generating EFI Capsule files
> +------------------------------------------------------
> +
> +This is an entry for generating EFI capsules.
> +
> +The parameters needed for generation of the capsules can either be
> +provided separately, or through a config file.
> +
> +Properties / Entry arguments:

Please add more detail to answer below questions.

> +    - cfg-file: Config file for providing capsule
> +      parameters.

What parameters? Please provide a link

> +    - image-index: Unique number for identifying
> +      corresponding payload image.

Is this indexed from 0 ?

> +    - image-type-id: Image GUID which will be used
> +      for identifying the image.

In what way is it identifying it? Is it identifying the board that the
image is for, or something else?

> +    - hardware-instance: Optional number for identifying
> +      unique hardware instance of a device in the system.

Is this numbered from 0?

> +    - monotomic-count: Count used when signing an image.

monotonic

What count? What is it used for?

> +    - private-key: Path to private key file.

File format?

> +    - pub-key-cert: Path to public key certificate file.

File format?

> +    - filename: Path to the input(payload) file.

This is just a binary file to be signed, right?

> +    - capsule: Path to the output capsule file.

File format?

> +
> +
> +
>  .. _etype_cbfs:
>
>  Entry: cbfs: Coreboot Filesystem (CBFS)
> diff --git a/tools/binman/etype/capsule.py b/tools/binman/etype/capsule.py
> new file mode 100644
> index 0000000000..a5ada885a6
> --- /dev/null
> +++ b/tools/binman/etype/capsule.py
> @@ -0,0 +1,102 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +# Copyright (c) 2023 Linaro Limited
> +#
> +# Entry-type module for producing a capsule
> +#
> +
> +import os

blank line here (I think it is the convention to put a blank line
after OS includes)

> +from u_boot_pylib import tools
> +from dtoc import fdt_util
> +from binman.entry import Entry

We should order these in reverse. I suspect that the conversion I did
from patman to u_boot_pylib was not correct, as I probably forgot to
fix the order.

> +
> +class Entry_capsule(Entry):
> +    """Entry for generating EFI capsules
> +
> +    This is an entry for generating EFI capsules.
> +
> +    The parameters needed for generation of the capsules can
> +    either be provided separately, or through a config file.
> +
> +    Properties / Entry arguments:
> +        - cfg-file: Config file for providing capsule
> +          parameters.
> +        - image-index: Unique number for identifying
> +          corresponding payload image.
> +        - image-type-id: Image GUID which will be used
> +          for identifying the image.
> +        - hardware-instance: Optional number for identifying
> +          unique hardware instance of a device in the system.
> +        - monotomic-count: Count used when signing an image.
> +        - private-key: Path to private key file.
> +        - pub-key-cert: Path to public key certificate file.
> +        - filename: Path to the input(payload) file.
> +        - capsule: Path to the output capsule file.

copy docs from above to here, when updated

> +
> +    """
> +    def __init__(self, section, etype, node):
> +        super().__init__(section, etype, node)
> +        self.image_index = 0
> +        self.image_guid = ""
> +        self.hardware_instance = 0
> +        self.monotonic_count = 0
> +        self.private_key = ""
> +        self.pub_key_cert = ""
> +        self.auth = 0
> +        self.payload = ""
> +        self.capsule_fname = ""
> +
> +    def ReadNode(self):
> +        super().ReadNode()
> +
> +        self.cfg_file = fdt_util.GetString(self._node, 'cfg-file')
> +        if not self.cfg_file:
> +            self.image_index = fdt_util.GetInt(self._node, 'image-index')
> +            if not self.image_index:
> +                self.Raise('mkeficapsule must be provided an Image Index')
> +
> +            self.image_guid = fdt_util.GetString(self._node, 'image-type-id')
> +            if not self.image_guid:
> +                self.Raise('mkeficapsule must be provided an Image GUID')

You can handle this with something like this in your __init__():

self.required_props = ['image-index', 'image-type-id', ...]

Sadly the docs for required_props are wrong.

> +
> +            self.hardware_instance = fdt_util.GetInt(self._node, 'hardware-instance')
> +            self.monotonic_count = fdt_util.GetInt(self._node, 'monotonic-count')
> +
> +            self.private_key = fdt_util.GetString(self._node, 'private-key')
> +            self.pub_key_cert = fdt_util.GetString(self._node, 'pub-key-cert')
> +
> +            if ((self.private_key and not self.pub_key_cert) or (self.pub_key_cert and not self.private_key)):
> +                self.Raise('Both private-key and public key Certificate need to be provided')
> +            elif not (self.private_key and self.pub_key_cert):
> +                self.auth = 0
> +            else:
> +                self.auth = 1
> +
> +            self.payload = fdt_util.GetString(self._node, 'filename')
> +            if not self.payload:
> +                self.Raise('mkeficapsule must be provided an input filename(payload)')
> +
> +            self.capsule_fname = fdt_util.GetString(self._node, 'capsule')
> +            if not self.capsule_fname:
> +                self.capsule_fname = os.path.splitext(self.payload)[0] + '.capsule'
> +
> +    def ObtainContents(self):
> +        if self.cfg_file:
> +            self.mkeficapsule.capsule_cfg_file(self.cfg_file)
> +        elif self.auth:
> +            self.mkeficapsule.cmdline_auth_capsule(self.image_index,
> +                                                   self.image_guid,
> +                                                   self.hardware_instance,
> +                                                   self.monotonic_count,
> +                                                   self.private_key,
> +                                                   self.pub_key_cert,
> +                                                   self.payload,
> +                                                   self.capsule_fname)
> +        else:
> +            self.mkeficapsule.cmdline_capsule(self.image_index,
> +                                              self.image_guid,
> +                                              self.hardware_instance,
> +                                              self.payload,
> +                                              self.capsule_fname)

This does not actually obtain the contents. I think you need:

self.SetContents(...)

here.

> +
> +    def AddBintools(self, btools):
> +        self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule')
> --
> 2.34.1
>

Regards,
Simon
Sughosh Ganu July 11, 2023, 7:13 a.m. UTC | #2
hi Simon,

On Tue, 11 Jul 2023 at 03:08, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Sun, 9 Jul 2023 at 07:34, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > Add support in binman for generating capsules. The capsule parameters
> > can be specified either through a config file or through the capsule
> > binman entry.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> > Changes since V2:
> > * New patch which generates capsules through binman replacing the
> >   earlier make target.
> >
> >  tools/binman/btool/mkeficapsule.py |  91 +++++++++++++++++++++++++
> >  tools/binman/entries.rst           |  27 ++++++++
> >  tools/binman/etype/capsule.py      | 102 +++++++++++++++++++++++++++++
> >  3 files changed, 220 insertions(+)
> >  create mode 100644 tools/binman/btool/mkeficapsule.py
> >  create mode 100644 tools/binman/etype/capsule.py
>
> Please do check test coverage (binman test -T). You are missing quite
> a lot in two two files you have added.

I was aware of adding tests in binman, but since the capsules
generated through binman are getting tested in the capsule update
functionality, I thought this would be superfluous. If this is
mandatory, I will add the tests. Will also address the rest of your
comments for this patch.

-sughosh

>
> >
> > diff --git a/tools/binman/btool/mkeficapsule.py b/tools/binman/btool/mkeficapsule.py
> > new file mode 100644
> > index 0000000000..9f656c12cf
> > --- /dev/null
> > +++ b/tools/binman/btool/mkeficapsule.py
> > @@ -0,0 +1,91 @@
> > +# SPDX-License-Identifier: GPL-2.0+
> > +# Copyright 2023 Linaro Limited
> > +#
> > +"""Bintool implementation for mkeficapsule tool
> > +
> > +mkeficapsule is a tool used for generating EFI capsules.
> > +
> > +The following are the command-line options to be provided
> > +to the tool
> > +Usage: mkeficapsule [options] <image blob> <output file>
> > +Options:
> > +       -g, --guid <guid string>    guid for image blob type
> > +       -i, --index <index>         update image index
> > +       -I, --instance <instance>   update hardware instance
> > +       -p, --private-key <privkey file>  private key file
> > +       -c, --certificate <cert file>     signer's certificate file
> > +       -m, --monotonic-count <count>     monotonic count
> > +       -d, --dump_sig              dump signature (*.p7)
> > +       -A, --fw-accept  firmware accept capsule, requires GUID, no image blob
> > +       -R, --fw-revert  firmware revert capsule, takes no GUID, no image blob
> > +       -o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff
> > +       -f, --cfg-file <config file> config file with capsule parameters
> > +       -h, --help                  print a help message
> > +
> > +"""
> > +
> > +from binman import bintool
> > +
> > +class Bintoolmkeficapsule(bintool.Bintool):
> > +    """Handles the 'mkeficapsule' tool
> > +
> > +    This bintool is used for generating the EFI capsules. The
> > +    capsule generation parameters can either be specified through
> > +    command-line, or through a config file.
> > +
> > +    """
> > +    def __init__(self, name):
> > +        super().__init__(name, 'mkeficapsule tool for generating capsules')
> > +
> > +    def capsule_cfg_file(self, cfg_file):
>
> """Function comment
>
> Args:
>    cfg_file (str): ...
> """
>
> Please fix throughout
>
> > +
> > +        args = [
> > +            f'--cfg-file={cfg_file}'
> > +        ]
> > +        self.run_cmd(*args)
> > +
> > +    def cmdline_capsule(self, image_index, image_guid, hardware_instance,
> > +                        payload, output_fname):
> > +
> > +        args = [
> > +            f'--index={image_index}',
> > +            f'--guid={image_guid}',
> > +            f'--instance={hardware_instance}',
> > +            payload,
> > +            output_fname
> > +        ]
> > +        self.run_cmd(*args)
> > +
> > +    def cmdline_auth_capsule(self, image_index, image_guid, hardware_instance,
> > +                             monotonic_count, priv_key, pub_key,
> > +                             payload, output_fname):
> > +
> > +        args = [
> > +            f'--index={image_index}',
> > +            f'--guid={image_guid}',
> > +            f'--instance={hardware_instance}',
> > +            f'--monotonic-count={monotonic_count}',
> > +            f'--private-key={priv_key}',
> > +            f'--certificate={pub_key}',
> > +            payload,
> > +            output_fname
> > +        ]
> > +        self.run_cmd(*args)
> > +
> > +    def fetch(self, method):
> > +        """Fetch handler for mkeficapsule
> > +
> > +        This builds the tool from source
> > +
> > +        Returns:
> > +            tuple:
> > +                str: Filename of fetched file to copy to a suitable directory
> > +                str: Name of temp directory to remove, or None
> > +        """
> > +        if method != bintool.FETCH_BUILD:
> > +            return None
> > +        result = self.build_from_git(
> > +            'https://source.denx.de/u-boot/u-boot.git',
> > +            'tools',
> > +            'tools/mkeficapsule')
> > +        return result
>
> Can we just install u-boot-tools? But if not, this is OK.
>
> But it does not actually work:
>
> $ binman tool -f mkeficapsule
> Fetch: mkeficapsule
> - trying method: binary download
> - trying method: build from source
> - clone git repo 'https://source.denx.de/u-boot/u-boot.git' to
> '/tmp/binmanf.chfbsxc0'
> - build target 'tools'
> Exception: Error 2 running 'make -C /tmp/binmanf.chfbsxc0 -j 64 tools': ***
> *** Configuration file ".config" not found!
> ***
> *** Please run some configurator (e.g. "make oldconfig" or
> *** "make menuconfig" or "make xconfig").
> ***
> make[2]: *** [scripts/kconfig/Makefile:75: syncconfig] Error 1
> make[1]: *** [Makefile:576: syncconfig] Error 2
> make: *** No rule to make target 'include/config/auto.conf', needed by
> 'include/config/uboot.release'.  Stop.
>
> - failed to fetch with all methods
>
> I think you need a make tools_defconfig in there.
>
> > diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
> > index b71af801fd..9a263e8691 100644
> > --- a/tools/binman/entries.rst
> > +++ b/tools/binman/entries.rst
> > @@ -283,6 +283,33 @@ entry; similarly for SPL.
> >
> >
> >
> > +.. _etype_capsule:
> > +
> > +Entry: capsule: Entry for generating EFI Capsule files
> > +------------------------------------------------------
> > +
> > +This is an entry for generating EFI capsules.
> > +
> > +The parameters needed for generation of the capsules can either be
> > +provided separately, or through a config file.
> > +
> > +Properties / Entry arguments:
>
> Please add more detail to answer below questions.
>
> > +    - cfg-file: Config file for providing capsule
> > +      parameters.
>
> What parameters? Please provide a link
>
> > +    - image-index: Unique number for identifying
> > +      corresponding payload image.
>
> Is this indexed from 0 ?
>
> > +    - image-type-id: Image GUID which will be used
> > +      for identifying the image.
>
> In what way is it identifying it? Is it identifying the board that the
> image is for, or something else?
>
> > +    - hardware-instance: Optional number for identifying
> > +      unique hardware instance of a device in the system.
>
> Is this numbered from 0?
>
> > +    - monotomic-count: Count used when signing an image.
>
> monotonic
>
> What count? What is it used for?
>
> > +    - private-key: Path to private key file.
>
> File format?
>
> > +    - pub-key-cert: Path to public key certificate file.
>
> File format?
>
> > +    - filename: Path to the input(payload) file.
>
> This is just a binary file to be signed, right?
>
> > +    - capsule: Path to the output capsule file.
>
> File format?
>
> > +
> > +
> > +
> >  .. _etype_cbfs:
> >
> >  Entry: cbfs: Coreboot Filesystem (CBFS)
> > diff --git a/tools/binman/etype/capsule.py b/tools/binman/etype/capsule.py
> > new file mode 100644
> > index 0000000000..a5ada885a6
> > --- /dev/null
> > +++ b/tools/binman/etype/capsule.py
> > @@ -0,0 +1,102 @@
> > +# SPDX-License-Identifier: GPL-2.0+
> > +# Copyright (c) 2023 Linaro Limited
> > +#
> > +# Entry-type module for producing a capsule
> > +#
> > +
> > +import os
>
> blank line here (I think it is the convention to put a blank line
> after OS includes)
>
> > +from u_boot_pylib import tools
> > +from dtoc import fdt_util
> > +from binman.entry import Entry
>
> We should order these in reverse. I suspect that the conversion I did
> from patman to u_boot_pylib was not correct, as I probably forgot to
> fix the order.
>
> > +
> > +class Entry_capsule(Entry):
> > +    """Entry for generating EFI capsules
> > +
> > +    This is an entry for generating EFI capsules.
> > +
> > +    The parameters needed for generation of the capsules can
> > +    either be provided separately, or through a config file.
> > +
> > +    Properties / Entry arguments:
> > +        - cfg-file: Config file for providing capsule
> > +          parameters.
> > +        - image-index: Unique number for identifying
> > +          corresponding payload image.
> > +        - image-type-id: Image GUID which will be used
> > +          for identifying the image.
> > +        - hardware-instance: Optional number for identifying
> > +          unique hardware instance of a device in the system.
> > +        - monotomic-count: Count used when signing an image.
> > +        - private-key: Path to private key file.
> > +        - pub-key-cert: Path to public key certificate file.
> > +        - filename: Path to the input(payload) file.
> > +        - capsule: Path to the output capsule file.
>
> copy docs from above to here, when updated
>
> > +
> > +    """
> > +    def __init__(self, section, etype, node):
> > +        super().__init__(section, etype, node)
> > +        self.image_index = 0
> > +        self.image_guid = ""
> > +        self.hardware_instance = 0
> > +        self.monotonic_count = 0
> > +        self.private_key = ""
> > +        self.pub_key_cert = ""
> > +        self.auth = 0
> > +        self.payload = ""
> > +        self.capsule_fname = ""
> > +
> > +    def ReadNode(self):
> > +        super().ReadNode()
> > +
> > +        self.cfg_file = fdt_util.GetString(self._node, 'cfg-file')
> > +        if not self.cfg_file:
> > +            self.image_index = fdt_util.GetInt(self._node, 'image-index')
> > +            if not self.image_index:
> > +                self.Raise('mkeficapsule must be provided an Image Index')
> > +
> > +            self.image_guid = fdt_util.GetString(self._node, 'image-type-id')
> > +            if not self.image_guid:
> > +                self.Raise('mkeficapsule must be provided an Image GUID')
>
> You can handle this with something like this in your __init__():
>
> self.required_props = ['image-index', 'image-type-id', ...]
>
> Sadly the docs for required_props are wrong.
>
> > +
> > +            self.hardware_instance = fdt_util.GetInt(self._node, 'hardware-instance')
> > +            self.monotonic_count = fdt_util.GetInt(self._node, 'monotonic-count')
> > +
> > +            self.private_key = fdt_util.GetString(self._node, 'private-key')
> > +            self.pub_key_cert = fdt_util.GetString(self._node, 'pub-key-cert')
> > +
> > +            if ((self.private_key and not self.pub_key_cert) or (self.pub_key_cert and not self.private_key)):
> > +                self.Raise('Both private-key and public key Certificate need to be provided')
> > +            elif not (self.private_key and self.pub_key_cert):
> > +                self.auth = 0
> > +            else:
> > +                self.auth = 1
> > +
> > +            self.payload = fdt_util.GetString(self._node, 'filename')
> > +            if not self.payload:
> > +                self.Raise('mkeficapsule must be provided an input filename(payload)')
> > +
> > +            self.capsule_fname = fdt_util.GetString(self._node, 'capsule')
> > +            if not self.capsule_fname:
> > +                self.capsule_fname = os.path.splitext(self.payload)[0] + '.capsule'
> > +
> > +    def ObtainContents(self):
> > +        if self.cfg_file:
> > +            self.mkeficapsule.capsule_cfg_file(self.cfg_file)
> > +        elif self.auth:
> > +            self.mkeficapsule.cmdline_auth_capsule(self.image_index,
> > +                                                   self.image_guid,
> > +                                                   self.hardware_instance,
> > +                                                   self.monotonic_count,
> > +                                                   self.private_key,
> > +                                                   self.pub_key_cert,
> > +                                                   self.payload,
> > +                                                   self.capsule_fname)
> > +        else:
> > +            self.mkeficapsule.cmdline_capsule(self.image_index,
> > +                                              self.image_guid,
> > +                                              self.hardware_instance,
> > +                                              self.payload,
> > +                                              self.capsule_fname)
>
> This does not actually obtain the contents. I think you need:
>
> self.SetContents(...)
>
> here.
>
> > +
> > +    def AddBintools(self, btools):
> > +        self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule')
> > --
> > 2.34.1
> >
>
> Regards,
> Simon
Simon Glass July 11, 2023, 7:13 p.m. UTC | #3
Hi Sughosh,

On Tue, 11 Jul 2023 at 01:13, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Tue, 11 Jul 2023 at 03:08, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Sun, 9 Jul 2023 at 07:34, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > Add support in binman for generating capsules. The capsule parameters
> > > can be specified either through a config file or through the capsule
> > > binman entry.
> > >
> > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > ---
> > > Changes since V2:
> > > * New patch which generates capsules through binman replacing the
> > >   earlier make target.
> > >
> > >  tools/binman/btool/mkeficapsule.py |  91 +++++++++++++++++++++++++
> > >  tools/binman/entries.rst           |  27 ++++++++
> > >  tools/binman/etype/capsule.py      | 102 +++++++++++++++++++++++++++++
> > >  3 files changed, 220 insertions(+)
> > >  create mode 100644 tools/binman/btool/mkeficapsule.py
> > >  create mode 100644 tools/binman/etype/capsule.py
> >
> > Please do check test coverage (binman test -T). You are missing quite
> > a lot in two two files you have added.
>
> I was aware of adding tests in binman, but since the capsules
> generated through binman are getting tested in the capsule update
> functionality, I thought this would be superfluous. If this is
> mandatory, I will add the tests. Will also address the rest of your
> comments for this patch.

The binman tests are for binman, which is a self-contained program
with 100% test coverage. That is a very important feature, since lots
of random SoC-specific features depend on it. If something breaks, it
can be a bit of a disaster.

Also, 100% test coverage allows refactoring of binman's code without
worry about breaking something. This is also very important, since we
will find problems which the current code base cannot solve. People
need the confidence to refactor

In practice, the tests define the behaviour. Code which is not tested
is therefore considered 'dead' code and should be deleted.

A notable point with binman tests is that all failure modes are
tested, not just 'happy path'.

[..]

Regards,
Simon
diff mbox series

Patch

diff --git a/tools/binman/btool/mkeficapsule.py b/tools/binman/btool/mkeficapsule.py
new file mode 100644
index 0000000000..9f656c12cf
--- /dev/null
+++ b/tools/binman/btool/mkeficapsule.py
@@ -0,0 +1,91 @@ 
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2023 Linaro Limited
+#
+"""Bintool implementation for mkeficapsule tool
+
+mkeficapsule is a tool used for generating EFI capsules.
+
+The following are the command-line options to be provided
+to the tool
+Usage: mkeficapsule [options] <image blob> <output file>
+Options:
+	-g, --guid <guid string>    guid for image blob type
+	-i, --index <index>         update image index
+	-I, --instance <instance>   update hardware instance
+	-p, --private-key <privkey file>  private key file
+	-c, --certificate <cert file>     signer's certificate file
+	-m, --monotonic-count <count>     monotonic count
+	-d, --dump_sig              dump signature (*.p7)
+	-A, --fw-accept  firmware accept capsule, requires GUID, no image blob
+	-R, --fw-revert  firmware revert capsule, takes no GUID, no image blob
+	-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff
+	-f, --cfg-file <config file> config file with capsule parameters
+	-h, --help                  print a help message
+
+"""
+
+from binman import bintool
+
+class Bintoolmkeficapsule(bintool.Bintool):
+    """Handles the 'mkeficapsule' tool
+
+    This bintool is used for generating the EFI capsules. The
+    capsule generation parameters can either be specified through
+    command-line, or through a config file.
+
+    """
+    def __init__(self, name):
+        super().__init__(name, 'mkeficapsule tool for generating capsules')
+
+    def capsule_cfg_file(self, cfg_file):
+
+        args = [
+            f'--cfg-file={cfg_file}'
+        ]
+        self.run_cmd(*args)
+
+    def cmdline_capsule(self, image_index, image_guid, hardware_instance,
+                        payload, output_fname):
+
+        args = [
+            f'--index={image_index}',
+            f'--guid={image_guid}',
+            f'--instance={hardware_instance}',
+            payload,
+            output_fname
+        ]
+        self.run_cmd(*args)
+
+    def cmdline_auth_capsule(self, image_index, image_guid, hardware_instance,
+                             monotonic_count, priv_key, pub_key,
+                             payload, output_fname):
+
+        args = [
+            f'--index={image_index}',
+            f'--guid={image_guid}',
+            f'--instance={hardware_instance}',
+            f'--monotonic-count={monotonic_count}',
+            f'--private-key={priv_key}',
+            f'--certificate={pub_key}',
+            payload,
+            output_fname
+        ]
+        self.run_cmd(*args)
+
+    def fetch(self, method):
+        """Fetch handler for mkeficapsule
+
+        This builds the tool from source
+
+        Returns:
+            tuple:
+                str: Filename of fetched file to copy to a suitable directory
+                str: Name of temp directory to remove, or None
+        """
+        if method != bintool.FETCH_BUILD:
+            return None
+        result = self.build_from_git(
+            'https://source.denx.de/u-boot/u-boot.git',
+            'tools',
+            'tools/mkeficapsule')
+        return result
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
index b71af801fd..9a263e8691 100644
--- a/tools/binman/entries.rst
+++ b/tools/binman/entries.rst
@@ -283,6 +283,33 @@  entry; similarly for SPL.
 
 
 
+.. _etype_capsule:
+
+Entry: capsule: Entry for generating EFI Capsule files
+------------------------------------------------------
+
+This is an entry for generating EFI capsules.
+
+The parameters needed for generation of the capsules can either be
+provided separately, or through a config file.
+
+Properties / Entry arguments:
+    - cfg-file: Config file for providing capsule
+      parameters.
+    - image-index: Unique number for identifying
+      corresponding payload image.
+    - image-type-id: Image GUID which will be used
+      for identifying the image.
+    - hardware-instance: Optional number for identifying
+      unique hardware instance of a device in the system.
+    - monotomic-count: Count used when signing an image.
+    - private-key: Path to private key file.
+    - pub-key-cert: Path to public key certificate file.
+    - filename: Path to the input(payload) file.
+    - capsule: Path to the output capsule file.
+
+
+
 .. _etype_cbfs:
 
 Entry: cbfs: Coreboot Filesystem (CBFS)
diff --git a/tools/binman/etype/capsule.py b/tools/binman/etype/capsule.py
new file mode 100644
index 0000000000..a5ada885a6
--- /dev/null
+++ b/tools/binman/etype/capsule.py
@@ -0,0 +1,102 @@ 
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2023 Linaro Limited
+#
+# Entry-type module for producing a capsule
+#
+
+import os
+from u_boot_pylib import tools
+from dtoc import fdt_util
+from binman.entry import Entry
+
+class Entry_capsule(Entry):
+    """Entry for generating EFI capsules
+
+    This is an entry for generating EFI capsules.
+
+    The parameters needed for generation of the capsules can
+    either be provided separately, or through a config file.
+
+    Properties / Entry arguments:
+        - cfg-file: Config file for providing capsule
+          parameters.
+        - image-index: Unique number for identifying
+          corresponding payload image.
+        - image-type-id: Image GUID which will be used
+          for identifying the image.
+        - hardware-instance: Optional number for identifying
+          unique hardware instance of a device in the system.
+        - monotomic-count: Count used when signing an image.
+        - private-key: Path to private key file.
+        - pub-key-cert: Path to public key certificate file.
+        - filename: Path to the input(payload) file.
+        - capsule: Path to the output capsule file.
+
+    """
+    def __init__(self, section, etype, node):
+        super().__init__(section, etype, node)
+        self.image_index = 0
+        self.image_guid = ""
+        self.hardware_instance = 0
+        self.monotonic_count = 0
+        self.private_key = ""
+        self.pub_key_cert = ""
+        self.auth = 0
+        self.payload = ""
+        self.capsule_fname = ""
+
+    def ReadNode(self):
+        super().ReadNode()
+
+        self.cfg_file = fdt_util.GetString(self._node, 'cfg-file')
+        if not self.cfg_file:
+            self.image_index = fdt_util.GetInt(self._node, 'image-index')
+            if not self.image_index:
+                self.Raise('mkeficapsule must be provided an Image Index')
+
+            self.image_guid = fdt_util.GetString(self._node, 'image-type-id')
+            if not self.image_guid:
+                self.Raise('mkeficapsule must be provided an Image GUID')
+
+            self.hardware_instance = fdt_util.GetInt(self._node, 'hardware-instance')
+            self.monotonic_count = fdt_util.GetInt(self._node, 'monotonic-count')
+
+            self.private_key = fdt_util.GetString(self._node, 'private-key')
+            self.pub_key_cert = fdt_util.GetString(self._node, 'pub-key-cert')
+
+            if ((self.private_key and not self.pub_key_cert) or (self.pub_key_cert and not self.private_key)):
+                self.Raise('Both private-key and public key Certificate need to be provided')
+            elif not (self.private_key and self.pub_key_cert):
+                self.auth = 0
+            else:
+                self.auth = 1
+
+            self.payload = fdt_util.GetString(self._node, 'filename')
+            if not self.payload:
+                self.Raise('mkeficapsule must be provided an input filename(payload)')
+
+            self.capsule_fname = fdt_util.GetString(self._node, 'capsule')
+            if not self.capsule_fname:
+                self.capsule_fname = os.path.splitext(self.payload)[0] + '.capsule'
+
+    def ObtainContents(self):
+        if self.cfg_file:
+            self.mkeficapsule.capsule_cfg_file(self.cfg_file)
+        elif self.auth:
+            self.mkeficapsule.cmdline_auth_capsule(self.image_index,
+                                                   self.image_guid,
+                                                   self.hardware_instance,
+                                                   self.monotonic_count,
+                                                   self.private_key,
+                                                   self.pub_key_cert,
+                                                   self.payload,
+                                                   self.capsule_fname)
+        else:
+            self.mkeficapsule.cmdline_capsule(self.image_index,
+                                              self.image_guid,
+                                              self.hardware_instance,
+                                              self.payload,
+                                              self.capsule_fname)
+
+    def AddBintools(self, btools):
+        self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule')