| #!/usr/bin/env python3 |
| # Copyright 2023 The ChromiumOS Authors |
| # SPDX-License-Identifier: Apache-2.0 |
| import struct |
| import sys |
| |
| import elftools.elf.elffile |
| import elftools.elf.sections |
| |
| # Converts a zephyr.elf file into an extremely simple "image format" |
| # for device loading. Really we should just load the ELF file |
| # directly, but the python ChromeOS test image lacks elftools. Longer |
| # term we should probably just use rimage, but that's significantly |
| # harder to parse. |
| # |
| # Format: |
| # |
| # 1. Three LE 32 bit words: MagicNumber, SRAM len, BootAddress |
| # 2. Two byte arrays: SRAM (length specified), and DRAM (remainder of file) |
| # |
| # No padding or uninterpreted bytes. |
| |
| FILE_MAGIC = 0xE463BE95 |
| |
| elf_file = sys.argv[1] |
| out_file = sys.argv[2] |
| |
| sram = bytearray() |
| dram = bytearray() |
| |
| # Returns the offset of a segment within the sram region, or -1 if it |
| # doesn't appear to be SRAM. SRAM is mapped differently for different |
| # SOCs, but it's always a <=1M region in 0x4xxxxxxx. Just use what we |
| # get, but validate that it fits. |
| sram_block = 0 |
| |
| |
| def sram_off(addr): |
| global sram_block |
| if addr < 0x40000000 or addr >= 0x50000000: |
| return -1 |
| block = addr & ~0xFFFFF |
| assert sram_block in (0, block) |
| |
| sram_block = block |
| off = addr - sram_block |
| assert off < 0x100000 |
| return off |
| |
| |
| # Similar heuristics: current platforms put DRAM either at 0x60000000 |
| # or 0x90000000 with no more than 16M of range |
| def dram_off(addr): |
| if (addr >> 28 not in [6, 9]) or (addr & 0x0F000000 != 0): |
| return -1 |
| return addr & 0xFFFFFF |
| |
| |
| def read_elf(efile): |
| ef = elftools.elf.elffile.ELFFile(efile) |
| |
| for seg in ef.iter_segments(): |
| h = seg.header |
| if h.p_type == "PT_LOAD": |
| soff = sram_off(h.p_paddr) |
| doff = dram_off(h.p_paddr) |
| if soff >= 0: |
| buf = sram |
| off = soff |
| elif doff >= 0: |
| buf = dram |
| off = doff |
| else: |
| print(f"Invalid PT_LOAD address {h.p_paddr:x}") |
| sys.exit(1) |
| |
| dat = seg.data() |
| end = off + len(dat) |
| if end > len(buf): |
| buf.extend(b'\x00' * (end - len(buf))) |
| |
| # pylint: disable=consider-using-enumerate |
| for i in range(len(dat)): |
| buf[i + off] = dat[i] |
| |
| for sec in ef.iter_sections(): |
| if isinstance(sec, elftools.elf.sections.SymbolTableSection): |
| for sym in sec.iter_symbols(): |
| if sym.name == "mtk_adsp_boot_entry": |
| boot_vector = sym.entry['st_value'] |
| |
| with open(out_file, "wb") as of: |
| of.write(struct.pack("<III", FILE_MAGIC, len(sram), boot_vector)) |
| of.write(sram) |
| of.write(dram) |
| |
| |
| with open(elf_file, "rb") as f: |
| read_elf(f) |