1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
3#
4# Entry-type module for OP-TEE Trusted OS firmware blob
5#
6
7import struct
8
9from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg
10from binman import elf
11
12class Entry_tee_os(Entry_blob_named_by_arg):
13    """Entry containing an OP-TEE Trusted OS (TEE) blob
14
15    Properties / Entry arguments:
16        - tee-os-path: Filename of file to read into entry. This is typically
17            called tee.bin or tee.elf
18
19    This entry holds the run-time firmware, typically started by U-Boot SPL.
20    See the U-Boot README for your architecture or board for how to use it. See
21    https://github.com/OP-TEE/optee_os for more information about OP-TEE.
22
23    Note that if the file is in ELF format, it must go in a FIT. In that case,
24    this entry will mark itself as absent, providing the data only through the
25    read_elf_segments() method.
26
27    Marking this entry as absent means that it if is used in the wrong context
28    it can be automatically dropped. Thus it is possible to add an OP-TEE entry
29    like this::
30
31        binman {
32            tee-os {
33            };
34        };
35
36    and pass either an ELF or plain binary in with -a tee-os-path <filename>
37    and have binman do the right thing:
38
39       - include the entry if tee.bin is provided and it does NOT have the v1
40         header
41       - drop it otherwise
42
43    When used within a FIT, we can do::
44
45        binman {
46            fit {
47                tee-os {
48                };
49            };
50        };
51
52    which will split the ELF into separate nodes for each segment, if an ELF
53    file is provided (see :ref:`etype_fit`), or produce a single node if the
54    OP-TEE binary v1 format is provided (see optee_doc_) .
55
56    .. _optee_doc: https://optee.readthedocs.io/en/latest/architecture/core.html#partitioning-of-the-binary
57    """
58    def __init__(self, section, etype, node):
59        super().__init__(section, etype, node, 'tee-os')
60        self.external = True
61
62    @staticmethod
63    def is_optee_bin_v1(data):
64        return len(data) >= 8 and data[0:5] == b'OPTE\x01'
65
66    def ObtainContents(self, fake_size=0):
67        result = super().ObtainContents(fake_size)
68        if not self.missing:
69            # If using the flat binary (without the OP-TEE header), then it is
70            # just included as a blob. But if it is an ELF or usees the v1
71            # binary header, then the FIT implementation will call
72            # read_elf_segments() to get the segment information
73            if elf.is_valid(self.data):
74                self.mark_absent('uses Elf format which must be in a FIT')
75            elif self.is_optee_bin_v1(self.data):
76                # The FIT implementation will call read_elf_segments() to get
77                # the segment information
78                self.mark_absent('uses v1 format which must be in a FIT')
79        return result
80
81    def read_elf_segments(self):
82        data = self.GetData()
83        if self.is_optee_bin_v1(data):
84            # OP-TEE v1 format (tee.bin)
85            init_sz, start_hi, start_lo, _, paged_sz = (
86                struct.unpack_from('<5I', data, 0x8))
87            if paged_sz != 0:
88                self.Raise("OP-TEE paged mode not supported")
89            e_entry = (start_hi << 32) + start_lo
90            p_addr = e_entry
91            p_data = data[0x1c:]
92            if len(p_data) != init_sz:
93                self.Raise("Invalid OP-TEE file: size mismatch (expected %#x, have %#x)" %
94                           (init_sz, len(p_data)))
95            return [[0, p_addr, p_data]], e_entry
96        return None
97