xref: /optee_os/scripts/gen_tee_bin.py (revision 5966660c02b34dacb8ec40cd3c26a16a19287973)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (c) 2019, Linaro Limited
5#
6
7from __future__ import print_function
8from __future__ import division
9
10import argparse
11import sys
12import struct
13import re
14import hashlib
15try:
16    from elftools.elf.elffile import ELFFile
17    from elftools.elf.constants import SH_FLAGS
18    from elftools.elf.enums import ENUM_RELOC_TYPE_ARM
19    from elftools.elf.enums import ENUM_RELOC_TYPE_AARCH64
20    from elftools.elf.sections import SymbolTableSection
21    from elftools.elf.relocation import RelocationSection
22
23except ImportError:
24    print("""
25***
26Can't find elftools module. Probably it is not installed on your system.
27You can install this module with
28
29$ apt install python3-pyelftools
30
31if you are using Ubuntu. Or try to search for "pyelftools" or "elftools" in
32your package manager if you are using some other distribution.
33***
34""")
35    raise
36
37small_page_size = 4 * 1024
38elffile_symbols = None
39tee_pageable_bin = None
40tee_pager_bin = None
41tee_embdata_bin = None
42
43
44def eprint(*args, **kwargs):
45    print(*args, file=sys.stderr, **kwargs)
46
47
48def round_up(n, m):
49    if n == 0:
50        return 0
51    else:
52        return (((n - 1) // m) + 1) * m
53
54
55def get_arch_id(elffile):
56    e_machine = elffile.header['e_machine']
57    if e_machine == 'EM_ARM':
58        return 0
59    if e_machine == 'EM_AARCH64':
60        return 1
61    eprint('Unknown e_machine "%s"' % e_machine)
62    sys.exit(1)
63
64
65def get_symbol(elffile, name):
66    global elffile_symbols
67    if elffile_symbols is None:
68        elffile_symbols = dict()
69        symbol_tables = [s for s in elffile.iter_sections()
70                         if isinstance(s, SymbolTableSection)]
71        for section in symbol_tables:
72            for symbol in section.iter_symbols():
73                if symbol['st_info']['bind'] == 'STB_GLOBAL':
74                    elffile_symbols[symbol.name] = symbol
75
76    try:
77        return elffile_symbols[name]
78    except (KeyError):
79        eprint("Cannot find symbol %s" % name)
80        sys.exit(1)
81
82
83def get_sections(elffile, pad_to, dump_names):
84    last_end = 0
85    bin_data = bytearray()
86
87    for section in elffile.iter_sections():
88        if (section['sh_type'] == 'SHT_NOBITS' or
89                not (section['sh_flags'] & SH_FLAGS.SHF_ALLOC) or
90                not dump_names.match(section.name)):
91            continue
92
93        if last_end == 0:
94            bin_data = section.data()
95        else:
96            if section['sh_addr'] > last_end:
97                bin_data += bytearray(section['sh_addr'] - last_end)
98            bin_data += section.data()
99
100        last_end = section['sh_addr'] + section['sh_size']
101
102    if pad_to > last_end:
103        bin_data += bytearray(pad_to - last_end)
104        last_end = pad_to
105
106    return bin_data
107
108
109def get_pageable_bin(elffile):
110    global tee_pageable_bin
111    if tee_pageable_bin is None:
112        pad_to = 0
113        dump_names = re.compile(r'^\..*_(pageable|init)$')
114        tee_pageable_bin = get_sections(elffile, pad_to, dump_names)
115    return tee_pageable_bin
116
117
118def get_pager_bin(elffile):
119    global tee_pager_bin
120    if tee_pager_bin is None:
121        pad_to = get_symbol(elffile, '__data_end')['st_value']
122        dump_names = re.compile(
123            r'^\.(text|rodata|got|data|ARM\.exidx|ARM\.extab)$')
124        tee_pager_bin = get_sections(elffile, pad_to, dump_names)
125
126    return tee_pager_bin
127
128
129def get_reloc_bin(elffile):
130    if get_arch_id(elffile) == 0:
131        exp_rel_type = ENUM_RELOC_TYPE_ARM['R_ARM_RELATIVE']
132    else:
133        exp_rel_type = ENUM_RELOC_TYPE_AARCH64['R_AARCH64_RELATIVE']
134
135    link_address = get_symbol(elffile, '__text_start')['st_value']
136
137    addrs = []
138    for section in elffile.iter_sections():
139        if not isinstance(section, RelocationSection):
140            continue
141        for rel in section.iter_relocations():
142            if rel['r_info_type'] == 0:
143                continue
144            if rel['r_info_type'] != exp_rel_type:
145                eprint("Unexpected relocation type 0x%x" %
146                       rel['r_info_type'])
147                sys.exit(1)
148            addrs.append(rel['r_offset'] - link_address)
149
150    addrs.sort()
151    data = bytearray()
152    for a in addrs:
153        data += struct.pack('<I', a)
154
155    # Relocations has been reduced to only become the relative type with
156    # addend at the address (r_offset) of relocation, that is, increase by
157    # load_offset. The addresses (r_offset) are also sorted. The format is
158    # then:
159    # uint32_t: relocation #1
160    # uint32_t: relocation #2
161    # ...
162    # uint32_t: relocation #n
163
164    return data
165
166
167def get_hashes_bin(elffile):
168    pageable_bin = get_pageable_bin(elffile)
169    if len(pageable_bin) % small_page_size != 0:
170        eprint("pageable size not a multiple of 4K: "
171               "{}".format(paged_area_size))
172        sys.exit(1)
173
174    data = bytearray()
175    for n in range(0, len(pageable_bin), small_page_size):
176        page = pageable_bin[n:n + small_page_size]
177        data += hashlib.sha256(page).digest()
178
179    return data
180
181
182def get_embdata_bin(elffile):
183    global tee_embdata_bin
184    if tee_embdata_bin is None:
185        hashes_bin = get_hashes_bin(elffile)
186        reloc_bin = get_reloc_bin(elffile)
187
188        num_entries = 2
189        hash_offs = 2 * 4 + num_entries * (2 * 4)
190        hash_pad = round_up(len(hashes_bin), 8) - len(hashes_bin)
191        reloc_offs = hash_offs + len(hashes_bin) + hash_pad
192        reloc_pad = round_up(len(reloc_bin), 8) - len(reloc_bin)
193        total_len = reloc_offs + len(reloc_bin) + reloc_pad
194
195        tee_embdata_bin = struct.pack('<IIIIII', total_len, num_entries,
196                                      hash_offs, len(hashes_bin),
197                                      reloc_offs, len(reloc_bin))
198        tee_embdata_bin += hashes_bin + bytearray(hash_pad)
199        tee_embdata_bin += reloc_bin + bytearray(reloc_pad)
200
201    # The embedded data region is designed to be easy to extend when
202    # needed, it's formatted as:
203    # +---------------------------------------------------------+
204    # | uint32_t: Length of entire area including this field    |
205    # +---------------------------------------------------------+
206    # | uint32_t: Number of entries "2"                         |
207    # +---------------------------------------------------------+
208    # | uint32_t: Offset of hashes from beginning of table      |
209    # +---------------------------------------------------------+
210    # | uint32_t: Length of hashes                              |
211    # +---------------------------------------------------------+
212    # | uint32_t: Offset of relocations from beginning of table |
213    # +---------------------------------------------------------+
214    # | uint32_t: Length of relocations                         |
215    # +---------------------------------------------------------+
216    # | Data of hashes + eventual padding                       |
217    # +---------------------------------------------------------+
218    # | Data of relocations + eventual padding                  |
219    # +---------------------------------------------------------+
220
221    return tee_embdata_bin
222
223
224def output_pager_bin(elffile, outf):
225    outf.write(get_pager_bin(elffile))
226
227
228def output_pageable_bin(elffile, outf):
229    outf.write(get_pageable_bin(elffile))
230
231
232def get_init_load_addr(elffile):
233    init_load_addr = get_symbol(elffile, '_start')['st_value']
234    init_load_addr_hi = init_load_addr >> 32
235    init_load_addr_lo = init_load_addr & 0xffffffff
236    return init_load_addr_hi, init_load_addr_lo
237
238
239def output_header_v1(elffile, outf):
240    arch_id = get_arch_id(elffile)
241    pager_bin = get_pager_bin(elffile)
242    pageable_bin = get_pageable_bin(elffile)
243    embdata_bin = get_embdata_bin(elffile)
244    init_load_addr = get_init_load_addr(elffile)
245    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
246    pager_bin_size = len(pager_bin)
247    paged_area_size = len(pageable_bin)
248
249    init_mem_usage = (get_symbol(elffile, '__init_end')['st_value'] -
250                      get_symbol(elffile, '__text_start')['st_value'] +
251                      len(embdata_bin))
252
253    init_size = (pager_bin_size + min(init_bin_size, paged_area_size) +
254                 len(embdata_bin))
255    paged_size = paged_area_size - min(init_bin_size, paged_area_size)
256
257    magic = 0x4554504f  # 'OPTE'
258    version = 1
259    flags = 0
260    outf.write(struct.pack('<IBBHIIIII', magic, version, arch_id, flags,
261                           init_size, init_load_addr[0], init_load_addr[1],
262                           init_mem_usage, paged_size))
263    outf.write(pager_bin)
264    outf.write(pageable_bin[:init_bin_size])
265    outf.write(embdata_bin)
266    outf.write(pageable_bin[init_bin_size:])
267
268
269def output_header_v2(elffile, outf):
270    arch_id = get_arch_id(elffile)
271    init_load_addr = get_init_load_addr(elffile)
272    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
273    pager_bin_size = len(get_pager_bin(elffile))
274    paged_area_size = len(get_pageable_bin(elffile))
275    embdata_bin_size = len(get_embdata_bin(elffile))
276
277    init_size = (pager_bin_size + min(init_bin_size, paged_area_size) +
278                 embdata_bin_size)
279    paged_size = paged_area_size - min(init_bin_size, paged_area_size)
280
281    magic = 0x4554504f  # 'OPTE'
282    version = 2
283    flags = 0
284    nb_images = 1 if paged_size == 0 else 2
285    outf.write(struct.pack('<IBBHI', magic, version, arch_id, flags,
286                           nb_images))
287    outf.write(struct.pack('<IIII', init_load_addr[0], init_load_addr[1],
288                           0, init_size))
289    if nb_images == 2:
290        outf.write(struct.pack('<IIII', 0xffffffff, 0xffffffff, 1, paged_size))
291
292
293def output_pager_v2(elffile, outf):
294    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
295    pager_bin = get_pager_bin(elffile)
296    pageable_bin = get_pageable_bin(elffile)
297    embdata_bin = get_embdata_bin(elffile)
298
299    outf.write(pager_bin)
300    outf.write(pageable_bin[:init_bin_size])
301    outf.write(embdata_bin)
302
303
304def output_pageable_v2(elffile, outf):
305    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
306    outf.write(get_pageable_bin(elffile)[init_bin_size:])
307
308
309def get_args():
310    parser = argparse.ArgumentParser()
311
312    parser.add_argument('--input',
313                        required=True, type=argparse.FileType('rb'),
314                        help='The input tee.elf')
315
316    parser.add_argument('--out_tee_bin',
317                        required=False, type=argparse.FileType('wb'),
318                        help='The output tee.bin')
319
320    parser.add_argument('--out_tee_pager_bin',
321                        required=False, type=argparse.FileType('wb'),
322                        help='The output tee_pager.bin')
323
324    parser.add_argument('--out_tee_pageable_bin',
325                        required=False, type=argparse.FileType('wb'),
326                        help='The output tee_pageable.bin')
327
328    parser.add_argument('--out_header_v2',
329                        required=False, type=argparse.FileType('wb'),
330                        help='The output tee_header_v2.bin')
331
332    parser.add_argument('--out_pager_v2',
333                        required=False, type=argparse.FileType('wb'),
334                        help='The output tee_pager_v2.bin')
335
336    parser.add_argument('--out_pageable_v2',
337                        required=False, type=argparse.FileType('wb'),
338                        help='The output tee_pageable_v2.bin')
339
340    return parser.parse_args()
341
342
343def main():
344    args = get_args()
345
346    elffile = ELFFile(args.input)
347
348    if args.out_tee_bin:
349        output_header_v1(elffile, args.out_tee_bin)
350
351    if args.out_tee_pager_bin:
352        output_pager_bin(elffile, args.out_tee_pager_bin)
353
354    if args.out_tee_pageable_bin:
355        output_pageable_bin(elffile, args.out_tee_pageable_bin)
356
357    if args.out_header_v2:
358        output_header_v2(elffile, args.out_header_v2)
359
360    if args.out_pager_v2:
361        output_pager_v2(elffile, args.out_pager_v2)
362
363    if args.out_pageable_v2:
364        output_pageable_v2(elffile, args.out_pageable_v2)
365
366
367if __name__ == "__main__":
368    main()
369