All of lore.kernel.org
 help / color / mirror / Atom feed
From: Quentin Schulz <quentin.schulz@theobroma-systems.com>
To: Simon Glass <sjg@chromium.org>,
	U-Boot Mailing List <u-boot@lists.denx.de>
Cc: Tom Rini <trini@konsulko.com>, huang lin <hl@rock-chips.com>,
	Jeffy Chen <jeffy.chen@rock-chips.com>,
	Kever Yang <kever.yang@rock-chips.com>,
	Ivan Mikhaylov <ivan.mikhaylov@siemens.com>,
	Roger Quadros <rogerq@ti.com>,
	Philippe Reynes <philippe.reynes@softathome.com>,
	Alper Nebi Yasak <alpernebiyasak@gmail.com>,
	Jerome Forissier <jerome.forissier@linaro.org>,
	Peter Geis <pgwipeout@gmail.com>
Subject: Re: [PATCH v8 06/13] binman: Support new op-tee binary format
Date: Mon, 2 Jan 2023 18:53:59 +0100	[thread overview]
Message-ID: <86fabb1a-5087-263f-fa51-87c34d99505a@theobroma-systems.com> (raw)
In-Reply-To: <20221221230726.638740-7-sjg@chromium.org>

Hi Simon,

On 12/22/22 00:07, Simon Glass wrote:
> OP-TEE has a format with a binary header that can be used instead of the
> ELF file. With newer versions of OP-TEE this may be required on some
> platforms.
> 
> Add support for this in binman. First, add a method to obtain the ELF
> sections from an entry, then use that in the FIT support. We then end up
> with the ability to support both types of OP-TEE files, depending on which
> one is passed in with the entry argument (TEE=xxx in the U-Boot build).
> 
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
> 
> (no changes since v7)
> 
> Changes in v7:
> - Correct missing test coverage
> 
> Changes in v6:
> - Update op-tee to support new v1 binary header
> 
>   tools/binman/entries.rst                     | 35 ++++++++-
>   tools/binman/entry.py                        | 13 +++
>   tools/binman/etype/fit.py                    | 69 +++++++++-------
>   tools/binman/etype/section.py                |  9 +++
>   tools/binman/etype/tee_os.py                 | 68 +++++++++++++++-
>   tools/binman/ftest.py                        | 83 ++++++++++++++++++++
>   tools/binman/test/263_tee_os_opt.dts         | 22 ++++++
>   tools/binman/test/264_tee_os_opt_fit.dts     | 33 ++++++++
>   tools/binman/test/265_tee_os_opt_fit_bad.dts | 40 ++++++++++
>   9 files changed, 340 insertions(+), 32 deletions(-)
>   create mode 100644 tools/binman/test/263_tee_os_opt.dts
>   create mode 100644 tools/binman/test/264_tee_os_opt_fit.dts
>   create mode 100644 tools/binman/test/265_tee_os_opt_fit_bad.dts
> 
> diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
> index b2ce7960d3b..a3e4493a44f 100644
> --- a/tools/binman/entries.rst
> +++ b/tools/binman/entries.rst
> @@ -1508,12 +1508,45 @@ Entry: tee-os: Entry containing an OP-TEE Trusted OS (TEE) blob
>   
>   Properties / Entry arguments:
>       - tee-os-path: Filename of file to read into entry. This is typically
> -        called tee-pager.bin
> +        called tee.bin or tee.elf
>   
>   This entry holds the run-time firmware, typically started by U-Boot SPL.
>   See the U-Boot README for your architecture or board for how to use it. See
>   https://urldefense.com/v3/__https://github.com/OP-TEE/optee_os__;!!OOPJP91ZZw!kx6SLv4sPusmg1TyYMw-Ho5G9jxeVMf9HOYx_4yq3ZNl_TpoGJoUyICZMgEiv0Zd6l_Bl8f5OAFYyJxm8wDUevhARIs$  for more information about OP-TEE.
>   
> +Note that if the file is in ELF format, it must go in a FIT. In that case,
> +this entry will mark itself as absent, providing the data only through the
> +read_elf_segments() method.
> +
> +Marking this entry as absent means that it if is used in the wrong context
> +it can be automatically dropped. Thus it is possible to add anb OP-TEE entry

s/anb/an/

> +like this::
> +
> +    binman {
> +        tee-os {
> +        };
> +    };
> +
> +and pass either an ELF or plain binary in with -a tee-os-path <filename>
> +and have binman do the right thing:
> +
> +   - include the entry if tee.bin is provided and it doesn't have the v1
> +     header
> +   - drop it otherwise
> +

Is there an actual usecase for this? (sorry if this was mentioned in the 
earlier versions of the patch) Are we expecting to be able to append the 
content of tee-os to some raw binary instead of putting OP-TEE OS in a 
u-boot.itb image?

> +When used within a FIT, we can do::
> +
> +    binman {
> +        fit {
> +            tee-os {
> +            };
> +        };
> +    };
> +
> +which will split the ELF into separate nodes for each segment, if an ELF
> +file is provide (see Flat Image Tree / FIT), or produce a single node if

s/provide/provided/

You can use a reference here since we have a _etype_fit target for "Flat 
Image Tree / FIT".

> +the binary v1 format is provided.
> +
>   

I'm a bit worried this is OP-TEE OS specific? We could also point to the 
documentation here: 
https://optee.readthedocs.io/en/latest/architecture/core.html#partitioning-of-the-binary?

>   
>   .. _etype_text:
> diff --git a/tools/binman/entry.py b/tools/binman/entry.py
> index 637aece3705..de51d295891 100644
> --- a/tools/binman/entry.py
> +++ b/tools/binman/entry.py
> @@ -1290,3 +1290,16 @@ features to produce new behaviours.
>       def mark_absent(self, msg):
>           tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
>           self.absent = True
> +
> +    def read_elf_segments(self):
> +        """Read segments from an entry that can generate an ELF file
> +
> +        Returns:
> +            tuple:
> +                list of segments, each:
> +                    int: Segment number (0 = first)
> +                    int: Start address of segment in memory
> +                    bytes: Contents of segment
> +                int: entry address of ELF file
> +        """
> +        return None

Does it really make sense to have this function available to all Entry 
objects?

> diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py
> index 8ad4f3a8a83..21c769a1cbe 100644
> --- a/tools/binman/etype/fit.py
> +++ b/tools/binman/etype/fit.py
> @@ -540,41 +540,34 @@ class Entry_fit(Entry_section):
>                       else:
>                           self.Raise("Generator node requires 'fit,fdt-list' property")
>   
> -        def _gen_split_elf(base_node, node, elf_data, missing):
> +        def _gen_split_elf(base_node, node, segments, entry_addr):
>               """Add nodes for the ELF file, one per group of contiguous segments
>   
>               Args:
>                   base_node (Node): Template node from the binman definition
>                   node (Node): Node to replace (in the FIT being built)
> -                data (bytes): ELF-format data to process (may be empty)
> -                missing (bool): True if any of the data is missing
>   
> +                segments, entry_addr

Please document.

> +
> +                data (bytes): ELF-format data to process (may be empty)
>               """
> -            # If any pieces are missing, skip this. The missing entries will
> -            # show an error
> -            if not missing:
> -                try:
> -                    segments, entry = elf.read_loadable_segments(elf_data)
> -                except ValueError as exc:
> -                    self._raise_subnode(node,
> -                                        f'Failed to read ELF file: {str(exc)}')
> -                for (seq, start, data) in segments:
> -                    node_name = node.name[1:].replace('SEQ', str(seq + 1))
> -                    with fsw.add_node(node_name):
> -                        loadables.append(node_name)
> -                        for pname, prop in node.props.items():
> -                            if not pname.startswith('fit,'):
> -                                fsw.property(pname, prop.bytes)
> -                            elif pname == 'fit,load':
> -                                fsw.property_u32('load', start)
> -                            elif pname == 'fit,entry':
> -                                if seq == 0:
> -                                    fsw.property_u32('entry', entry)
> -                            elif pname == 'fit,data':
> -                                fsw.property('data', bytes(data))
> -                            elif pname != 'fit,operation':
> -                                self._raise_subnode(
> -                                    node, f"Unknown directive '{pname}'")
> +            for (seq, start, data) in segments:
> +                node_name = node.name[1:].replace('SEQ', str(seq + 1))
> +                with fsw.add_node(node_name):
> +                    loadables.append(node_name)
> +                    for pname, prop in node.props.items():
> +                        if not pname.startswith('fit,'):
> +                            fsw.property(pname, prop.bytes)
> +                        elif pname == 'fit,load':
> +                            fsw.property_u32('load', start)
> +                        elif pname == 'fit,entry':
> +                            if seq == 0:
> +                                fsw.property_u32('entry', entry_addr)
> +                        elif pname == 'fit,data':
> +                            fsw.property('data', bytes(data))
> +                        elif pname != 'fit,operation':
> +                            self._raise_subnode(
> +                                node, f"Unknown directive '{pname}'")
>   
>           def _gen_node(base_node, node, depth, in_images, entry):
>               """Generate nodes from a template
> @@ -598,6 +591,8 @@ class Entry_fit(Entry_section):
>                   depth (int): Current node depth (0 is the base 'fit' node)
>                   in_images (bool): True if this is inside the 'images' node, so
>                       that 'data' properties should be generated
> +                entry (entry_Section): Entry for the second containing the

s/second/section/ ?

> +                    contents of this node
>               """
>               oper = self._get_operation(base_node, node)
>               if oper == OP_GEN_FDT_NODES:
> @@ -609,10 +604,24 @@ class Entry_fit(Entry_section):
>                   missing_list = []
>                   entry.ObtainContents()
>                   entry.Pack(0)
> -                data = entry.GetData()
>                   entry.CheckMissing(missing_list)
>   
> -                _gen_split_elf(base_node, node, data, bool(missing_list))
> +                # If any pieces are missing, skip this. The missing entries will
> +                # show an error
> +                if not missing_list:
> +                    segs = entry.read_elf_segments()
> +                    if segs:
> +                        segments, entry_addr = segs
> +                    else:
> +                        elf_data = entry.GetData()
> +                        try:
> +                            segments, entry_addr = (
> +                                    elf.read_loadable_segments(elf_data))
> +                        except ValueError as exc:
> +                            self._raise_subnode(
> +                                node, f'Failed to read ELF file: {str(exc)}')
> +
> +                    _gen_split_elf(base_node, node, segments, entry_addr)
>   
>           def _add_node(base_node, depth, node):
>               """Add nodes to the output FIT
> diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py
> index dcb7a062047..57bfee0b286 100644
> --- a/tools/binman/etype/section.py
> +++ b/tools/binman/etype/section.py
> @@ -948,3 +948,12 @@ class Entry_section(Entry):
>           super().AddBintools(btools)
>           for entry in self._entries.values():
>               entry.AddBintools(btools)
> +
> +    def read_elf_segments(self):
> +        entries = self.GetEntries()
> +
> +        # If the section only has one entry, see if it can provide ELF segments
> +        if len(entries) == 1:
> +            for entry in entries.values():
> +                return entry.read_elf_segments()
> +        return None
> diff --git a/tools/binman/etype/tee_os.py b/tools/binman/etype/tee_os.py
> index 6ce4b672de4..687acd4689f 100644
> --- a/tools/binman/etype/tee_os.py
> +++ b/tools/binman/etype/tee_os.py
> @@ -4,19 +4,85 @@
>   # Entry-type module for OP-TEE Trusted OS firmware blob
>   #
>   
> +import struct
> +
>   from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg
> +from binman import elf
>   
>   class Entry_tee_os(Entry_blob_named_by_arg):
>       """Entry containing an OP-TEE Trusted OS (TEE) blob
>   
>       Properties / Entry arguments:
>           - tee-os-path: Filename of file to read into entry. This is typically
> -            called tee-pager.bin
> +            called tee.bin or tee.elf
>   
>       This entry holds the run-time firmware, typically started by U-Boot SPL.
>       See the U-Boot README for your architecture or board for how to use it. See
>       https://urldefense.com/v3/__https://github.com/OP-TEE/optee_os__;!!OOPJP91ZZw!kx6SLv4sPusmg1TyYMw-Ho5G9jxeVMf9HOYx_4yq3ZNl_TpoGJoUyICZMgEiv0Zd6l_Bl8f5OAFYyJxm8wDUevhARIs$  for more information about OP-TEE.
> +
> +    Note that if the file is in ELF format, it must go in a FIT. In that case,
> +    this entry will mark itself as absent, providing the data only through the
> +    read_elf_segments() method.
> +
> +    Marking this entry as absent means that it if is used in the wrong context
> +    it can be automatically dropped. Thus it is possible to add anb OP-TEE entry
> +    like this::
> +
> +        binman {
> +            tee-os {
> +            };
> +        };
> +
> +    and pass either an ELF or plain binary in with -a tee-os-path <filename>
> +    and have binman do the right thing:
> +
> +       - include the entry if tee.bin is provided and it doesn't have the v1

"does NOT" ??

> +         header
> +       - drop it otherwise
> +
> +    When used within a FIT, we can do::
> +
> +        binman {
> +            fit {
> +                tee-os {
> +                };
> +            };
> +        };
> +
> +    which will split the ELF into separate nodes for each segment, if an ELF
> +    file is provide (see Flat Image Tree / FIT), or produce a single node if
> +    the binary v1 format is provided.
>       """
>       def __init__(self, section, etype, node):
>           super().__init__(section, etype, node, 'tee-os')
>           self.external = True
> +
> +    @staticmethod
> +    def is_optee_bin(data):

Here you are checking it's a binary with v1 header, so please explicit 
in the function name. (there's already a v2 header available FWIW).

> +        return len(data) >= 8 and data[0:5] == b'OPTE\x01'
> +
> +    def ObtainContents(self, fake_size=0):
> +        super().ObtainContents(fake_size)

Do not silence the return code of the parent class here? I know it's 
only returning True ATM but nothing guarantees it'll stay this way.

> +        if not self.missing:
> +            if elf.is_valid(self.data):
> +                self.mark_absent('uses Elf format which must be in a FIT')
> +            elif self.is_optee_bin(self.data):
> +                self.mark_absent('uses v1 format which must be in a FIT')

What should this support then if neither ELF not binary with v1 header 
are supported? I don't see support for binary with v2 header anywhere so 
I'm quite confused by what this piece of code is supposed to handle?

I'm also very much against displaying a warning if the user has TEE set 
in their environment and the file exists but it won't be used in the 
final image. If it's not compatible based on the binman DT node, error 
out. If it's the wrong file or it's missing, error out. Is there some 
scenario where displaying a warning (and/or removing the entry with 
mark_absent like you did here) make sense?

In any case, thanks Simon and Jerome for looking into this, it's a 
bigger task than I had anticipated but am looking forward to this 
Rockchip-specific behavior to be dropped from mainline :)

Cheers,
Quentin

  parent reply	other threads:[~2023-01-02 17:54 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-21 23:07 [PATCH v8 00/13] binman: rockchip: Migrate from rockchip SPL_FIT_GENERATOR script Simon Glass
2022-12-21 23:07 ` [PATCH v8 01/13] binman: Allow writing section contents to a file Simon Glass
2022-12-21 23:07 ` [PATCH v8 02/13] binman: Tidy up comment in fit _gen_node Simon Glass
2022-12-21 23:07 ` [PATCH v8 03/13] binman: Update entry docs Simon Glass
2023-01-02 16:19   ` Quentin Schulz
2022-12-21 23:07 ` [PATCH v8 04/13] binman: Support optional entries Simon Glass
2022-12-21 23:07 ` [PATCH v8 05/13] binman: Add a way to check for a valid ELF file Simon Glass
2023-01-02 16:26   ` Quentin Schulz
2022-12-21 23:07 ` [PATCH v8 06/13] binman: Support new op-tee binary format Simon Glass
2022-12-22 15:36   ` Jerome Forissier
2022-12-22 20:18     ` Simon Glass
2022-12-22 20:23       ` Simon Glass
2022-12-22 22:20         ` Jerome Forissier
2023-01-07 18:55           ` Simon Glass
2023-01-07 21:58             ` Jerome Forissier
2023-01-02 17:53   ` Quentin Schulz [this message]
2023-01-07 18:55     ` Simon Glass
2023-01-07 21:51       ` Jerome Forissier
2022-12-21 23:07 ` [PATCH v8 07/13] binman: Support optional external blobs Simon Glass
2022-12-21 23:07 ` [PATCH v8 08/13] rockchip: evb-rk3288: Drop raw-image support Simon Glass
2022-12-21 23:07 ` [PATCH v8 09/13] rockchip: Use multiple-images for rk3399 Simon Glass
2023-01-02 16:42   ` Quentin Schulz
2023-01-04 20:01     ` Simon Glass
2023-01-05  9:47       ` Quentin Schulz
2023-01-07  0:13         ` Simon Glass
2022-12-21 23:07 ` [PATCH v8 10/13] rockchip: Support building the all output files in binman Simon Glass
2022-12-21 23:07 ` [PATCH v8 11/13] rockchip: Convert all boards to use binman Simon Glass
2022-12-21 23:07 ` [PATCH v8 12/13] rockchip: Drop the FIT generator script Simon Glass
2022-12-21 23:07 ` [PATCH v8 13/13] treewide: Disable USE_SPL_FIT_GENERATOR by default Simon Glass
2022-12-21 23:11   ` Tom Rini
2022-12-22 17:41     ` Simon Glass
2022-12-22 17:57       ` Tom Rini
2022-12-22 19:25         ` Simon Glass

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86fabb1a-5087-263f-fa51-87c34d99505a@theobroma-systems.com \
    --to=quentin.schulz@theobroma-systems.com \
    --cc=alpernebiyasak@gmail.com \
    --cc=hl@rock-chips.com \
    --cc=ivan.mikhaylov@siemens.com \
    --cc=jeffy.chen@rock-chips.com \
    --cc=jerome.forissier@linaro.org \
    --cc=kever.yang@rock-chips.com \
    --cc=pgwipeout@gmail.com \
    --cc=philippe.reynes@softathome.com \
    --cc=rogerq@ti.com \
    --cc=sjg@chromium.org \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.