1#!/usr/bin/env python3
2# Copyright 2023 The ChromiumOS Authors
3# SPDX-License-Identifier: Apache-2.0
4import struct
5import sys
6
7import elftools.elf.elffile
8import elftools.elf.sections
9
10# Converts a zephyr.elf file into an extremely simple "image format"
11# for device loading.  Really we should just load the ELF file
12# directly, but the python ChromeOS test image lacks elftools.  Longer
13# term we should probably just use rimage, but that's significantly
14# harder to parse.
15#
16# Format:
17#
18# 1. Three LE 32 bit words: MagicNumber, SRAM len, BootAddress
19# 2. Two byte arrays: SRAM (length specified), and DRAM (remainder of file)
20#
21# No padding or uninterpreted bytes.
22
23FILE_MAGIC = 0xE463BE95
24
25elf_file = sys.argv[1]
26out_file = sys.argv[2]
27
28sram = bytearray()
29dram = bytearray()
30
31# Returns the offset of a segment within the sram region, or -1 if it
32# doesn't appear to be SRAM.  SRAM is mapped differently for different
33# SOCs, but it's always a <=1M region in 0x4xxxxxxx.  Just use what we
34# get, but validate that it fits.
35sram_block = 0
36
37
38def sram_off(addr):
39    global sram_block
40    if addr < 0x40000000 or addr >= 0x50000000:
41        return -1
42    block = addr & ~0xFFFFF
43    assert sram_block in (0, block)
44
45    sram_block = block
46    off = addr - sram_block
47    assert off < 0x100000
48    return off
49
50
51# Similar heuristics: current platforms put DRAM either at 0x60000000
52# or 0x90000000 with no more than 16M of range
53def dram_off(addr):
54    if (addr >> 28 not in [6, 9]) or (addr & 0x0F000000 != 0):
55        return -1
56    return addr & 0xFFFFFF
57
58
59def read_elf(efile):
60    ef = elftools.elf.elffile.ELFFile(efile)
61
62    for seg in ef.iter_segments():
63        h = seg.header
64        if h.p_type == "PT_LOAD":
65            soff = sram_off(h.p_paddr)
66            doff = dram_off(h.p_paddr)
67            if soff >= 0:
68                buf = sram
69                off = soff
70            elif doff >= 0:
71                buf = dram
72                off = doff
73            else:
74                print(f"Invalid PT_LOAD address {h.p_paddr:x}")
75                sys.exit(1)
76
77            dat = seg.data()
78            end = off + len(dat)
79            if end > len(buf):
80                buf.extend(b'\x00' * (end - len(buf)))
81
82            # pylint: disable=consider-using-enumerate
83            for i in range(len(dat)):
84                buf[i + off] = dat[i]
85
86    for sec in ef.iter_sections():
87        if isinstance(sec, elftools.elf.sections.SymbolTableSection):
88            for sym in sec.iter_symbols():
89                if sym.name == "mtk_adsp_boot_entry":
90                    boot_vector = sym.entry['st_value']
91
92    with open(out_file, "wb") as of:
93        of.write(struct.pack("<III", FILE_MAGIC, len(sram), boot_vector))
94        of.write(sram)
95        of.write(dram)
96
97
98with open(elf_file, "rb") as f:
99    read_elf(f)
100