mirror of
https://github.com/pvvx/RTL00_WEB.git
synced 2024-12-24 15:05:17 +00:00
add
This commit is contained in:
parent
d1e4ee31f1
commit
a999e4c2fd
1 changed files with 345 additions and 0 deletions
345
USDK/rtlaimage.py
Normal file
345
USDK/rtlaimage.py
Normal file
|
@ -0,0 +1,345 @@
|
|||
#!/usr/bin/env python
|
||||
# (RTL8195AM/RTL8711AM/RTL8711AF/RTL8710AF) RtlImages Utility
|
||||
# pvvx
|
||||
|
||||
from __future__ import print_function, division
|
||||
from operator import attrgetter
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
|
||||
__version__ = "22.01.18"
|
||||
|
||||
PYTHON2 = sys.version_info[0] < 3 # True if on pre-Python 3
|
||||
|
||||
HM_IS_HDRR = 1
|
||||
HM_IS_HDRD = 2
|
||||
HM_IS_BOOT = 4
|
||||
HM_IS_OTA = 8
|
||||
HM_IS_SRAM = 16
|
||||
HM_IS_SDRAM = 32
|
||||
|
||||
# Function to return nth byte of a bitstring
|
||||
# Different behaviour on Python 2 vs 3
|
||||
if PYTHON2:
|
||||
def byte(bitstr, index):
|
||||
return ord(bitstr[index])
|
||||
else:
|
||||
def byte(bitstr, index):
|
||||
return bitstr[index]
|
||||
|
||||
|
||||
class FatalError(RuntimeError):
|
||||
"""
|
||||
Wrapper class for runtime errors that aren't caused by internal bugs, but by
|
||||
xchip responses or input content.
|
||||
"""
|
||||
def __init__(self, message):
|
||||
RuntimeError.__init__(self, message)
|
||||
|
||||
@staticmethod
|
||||
def WithResult(message, result):
|
||||
"""
|
||||
Return a fatal error object that appends the hex values of
|
||||
'result' as a string formatted argument.
|
||||
"""
|
||||
message += " (result was %s)" % hexify(result)
|
||||
return FatalError(message)
|
||||
|
||||
class ImageSegment(object):
|
||||
""" Wrapper class for a segment in an RTL image
|
||||
(very similar to a section in an ELFImage also) """
|
||||
def __init__(self, addr, data, file_offs = None):
|
||||
self.addr = addr
|
||||
# pad all ImageSegments to at least 4 bytes length
|
||||
pad_mod = len(data) % 4
|
||||
if pad_mod != 0:
|
||||
data += b"\x00" * (4 - pad_mod)
|
||||
self.data = data
|
||||
self.file_offs = file_offs
|
||||
|
||||
def copy_with_new_addr(self, new_addr):
|
||||
""" Return a new ImageSegment with same data, but mapped at
|
||||
a new address. """
|
||||
return ImageSegment(new_addr, self.data, 0)
|
||||
|
||||
def __repr__(self):
|
||||
r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr)
|
||||
if self.file_offs is not None:
|
||||
r += " file_offs 0x%08x" % (self.file_offs)
|
||||
return r
|
||||
|
||||
class ELFSection(ImageSegment):
|
||||
""" Wrapper class for a section in an ELF image, has a section
|
||||
name as well as the common properties of an ImageSegment. """
|
||||
def __init__(self, name, addr, data, size):
|
||||
super(ELFSection, self).__init__(addr, data)
|
||||
self.name = name.decode("utf-8")
|
||||
self.size = size
|
||||
|
||||
def __repr__(self):
|
||||
return "%s %s" % (self.name, super(ELFSection, self).__repr__())
|
||||
|
||||
class ELFFile(object):
|
||||
SEC_TYPE_PROGBITS = 0x01
|
||||
SEC_TYPE_STRTAB = 0x03
|
||||
|
||||
def __init__(self, name):
|
||||
# Load sections from the ELF file
|
||||
self.name = name
|
||||
try:
|
||||
with open(self.name, 'rb') as f:
|
||||
self._read_elf_file(f)
|
||||
except:
|
||||
print('Error: Not open input ELF file <%s>!' % self.name)
|
||||
sys.exit(-1)
|
||||
|
||||
def get_section(self, section_name):
|
||||
for s in self.sections:
|
||||
if s.name == section_name:
|
||||
return s
|
||||
raise ValueError("No section %s in ELF file" % section_name)
|
||||
|
||||
def _read_elf_file(self, f):
|
||||
# read the ELF file header
|
||||
LEN_FILE_HEADER = 0x34
|
||||
try:
|
||||
(ident,_type,machine,_version,
|
||||
self.entrypoint,_phoff,shoff,_flags,
|
||||
_ehsize, _phentsize,_phnum,_shentsize,
|
||||
_shnum,shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER))
|
||||
except struct.error as e:
|
||||
raise FatalError("Failed to read a valid ELF header from %s: %s" % (self.name, e))
|
||||
|
||||
if byte(ident, 0) != 0x7f or ident[1:4] != b'ELF':
|
||||
raise FatalError("%s has invalid ELF magic header" % self.name)
|
||||
if machine != 0x28:
|
||||
raise FatalError("%s does not appear to be an RTL ELF file. e_machine=%04x" % (self.name, machine))
|
||||
self._read_sections(f, shoff, shstrndx, _shnum)
|
||||
|
||||
def _read_sections(self, f, section_header_offs, shstrndx, shnum):
|
||||
LEN_SEC_HEADER = 0x28
|
||||
f.seek(section_header_offs)
|
||||
# print("%d sections found in ELF file" % shnum)
|
||||
if shnum == 0 or shnum > 100:
|
||||
raise FatalError("%d sections found in ELF file!" % shnum)
|
||||
section_header = f.read(shnum *LEN_SEC_HEADER)
|
||||
if len(section_header) == 0:
|
||||
raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs)
|
||||
if len(section_header) != shnum *LEN_SEC_HEADER:
|
||||
print('WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header), LEN_SEC_HEADER))
|
||||
|
||||
# walk through the section header and extract all sections
|
||||
section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER)
|
||||
|
||||
def read_section_header(offs):
|
||||
name_offs,sec_type,_flags,lma,sec_offs,size = struct.unpack_from("<LLLLLL", section_header[offs:])
|
||||
# print('read_section_header at %08x' % offs)
|
||||
return (name_offs, sec_type, lma, size, sec_offs)
|
||||
all_sections = [read_section_header(offs) for offs in section_header_offsets]
|
||||
prog_sections = [s for s in all_sections if s[1] == ELFFile.SEC_TYPE_PROGBITS]
|
||||
|
||||
# search for the string table section
|
||||
if not shstrndx * LEN_SEC_HEADER in section_header_offsets:
|
||||
raise FatalError("ELF file has no STRTAB section at shstrndx %d" % shstrndx)
|
||||
_,sec_type,_,sec_size,sec_offs = read_section_header(shstrndx * LEN_SEC_HEADER)
|
||||
if sec_type != ELFFile.SEC_TYPE_STRTAB:
|
||||
print('WARNING: ELF file has incorrect STRTAB section type 0x%02x' % sec_type)
|
||||
f.seek(sec_offs)
|
||||
string_table = f.read(sec_size)
|
||||
|
||||
# build the real list of ELFSections by reading the actual section names from the
|
||||
# string table section, and actual data for each section from the ELF file itself
|
||||
def lookup_string(offs):
|
||||
raw = string_table[offs:]
|
||||
return raw[:raw.index(b'\x00')]
|
||||
|
||||
def read_data(offs, size):
|
||||
f.seek(offs)
|
||||
return f.read(size)
|
||||
|
||||
prog_sections = [ELFSection(lookup_string(n_offs), lma, read_data(offs, size), size) for (n_offs, _type, lma, size, offs) in prog_sections if lma != 0]
|
||||
self.sections = prog_sections
|
||||
|
||||
class xFirmwareImage(object):
|
||||
def __init__(self, fname, segnames, hmode, addrl, addrh):
|
||||
self.fname = fname
|
||||
self.segnames = segnames
|
||||
self.hm = hmode
|
||||
self.addrl = addrl
|
||||
self.addrh = addrh
|
||||
self.addr = None
|
||||
self.data = ''
|
||||
self.size = 0 # size code/data only (without header)
|
||||
self.imgsegs = []
|
||||
|
||||
def load(self, sections):
|
||||
imgsegs = [s for s in sections if s.name in self.segnames]
|
||||
if imgsegs:
|
||||
if len(imgsegs) > 1:
|
||||
sorted(imgsegs, key = attrgetter('addr'))
|
||||
elif len(imgsegs[0].data) == 0: # file size == 0
|
||||
self.addr = imgsegs[0].addr
|
||||
# print('Segment at 0x%08x,' % self.addr, 'size 0x00000000 copy to image', self.fname)
|
||||
sections.remove(imgsegs[0])
|
||||
return
|
||||
self.addr = imgsegs[0].addr
|
||||
s = imgsegs[len(imgsegs) - 1]
|
||||
self.size = s.addr + len(s.data) - self.addr
|
||||
# print('Segment at 0x%08x,' % self.addr, 'size 0x%08x' % self.size, 'copy to image', self.fname)
|
||||
if self.addr < self.addrl and self.addr > self.addrh:
|
||||
print('Segment Address Error!')
|
||||
return
|
||||
if self.size:
|
||||
if self.hm & HM_IS_HDRR:
|
||||
self.data = struct.pack(b'<LLLL', self.size, self.addr, 0x35393138, 0x31313738)
|
||||
elif self.hm & HM_IS_HDRD:
|
||||
self.data = struct.pack(b'<LLLL', self.size, self.addr, 0xffffffff, 0xffffffff)
|
||||
elif self.hm & HM_IS_BOOT: # boot
|
||||
self.data = struct.pack(b'<LLLLLLLL', 0x96969999, 0xFC66CC3F, 0x03CC33C0, 0x6231DCE5, self.size, self.addr, 0xffff002c, 0xffffffff)
|
||||
x = self.addr
|
||||
for s in imgsegs:
|
||||
while x < s.addr:
|
||||
self.data += '\0'
|
||||
x += 1
|
||||
self.data += s.data
|
||||
# print('Segment at 0x%08x,' % s.addr, 'size 0x%08x' % len(s.data), 'name', s.name, 'copy to image', self.fname)
|
||||
x += s.size
|
||||
sections.remove(s)
|
||||
|
||||
def save(self, outdir, flg_p):
|
||||
if self.size:
|
||||
# print('Segment at 0x%08x,' % self.addr, 'size 0x%08x' % self.size, 'save to', self.fname)
|
||||
try:
|
||||
if flg_p:
|
||||
with open(outdir + self.fname + '.p.bin', "wb") as f:
|
||||
f.write(self.data)
|
||||
f.close()
|
||||
else:
|
||||
with open(outdir + self.fname + '.bin', "wb") as f:
|
||||
if self.hm & (HM_IS_HDRR | HM_IS_HDRD):
|
||||
f.write(self.data[16:])
|
||||
elif self.hm & HM_IS_BOOT:
|
||||
f.write(self.data[32:])
|
||||
else:
|
||||
f.write(self.data)
|
||||
f.close()
|
||||
except:
|
||||
print('Error: Not write file <%s>!' % self.fname)
|
||||
sys.exit(-1)
|
||||
|
||||
def save_ota(self, f, fn, chks):
|
||||
if self.size:
|
||||
# print('Segment at 0x%08x,' % self.addr, 'size 0x%08x' % self.size, 'save to %s' % fn)
|
||||
try:
|
||||
f.write(self.data)
|
||||
i = 0
|
||||
while i < len(self.data):
|
||||
chks += byte(self.data, i)
|
||||
i += 1
|
||||
except:
|
||||
print('Error: Not write file %s!' % fn)
|
||||
sys.exit(-1)
|
||||
return chks
|
||||
|
||||
def save_sram(self, f, fn):
|
||||
if self.size:
|
||||
# print('Segment at 0x%08x,' % self.addr, 'size 0x%08x' % self.size, 'save to %s' % fn)
|
||||
try:
|
||||
if self.hm & HM_IS_BOOT:
|
||||
gap = 0x0b000
|
||||
i = len(self.data)
|
||||
if i > gap:
|
||||
print('Error: Segment %s is big!' % self.name)
|
||||
f.write(self.data)
|
||||
while i < gap:
|
||||
f.write('\xff')
|
||||
i += 1
|
||||
else:
|
||||
f.write(self.data)
|
||||
except:
|
||||
print('Error: Not write file %s!' % fn)
|
||||
sys.exit(-1)
|
||||
|
||||
def elf2image(args):
|
||||
|
||||
e = ELFFile(args.elffile) # ELFSection is a subclass of ImageSegment
|
||||
# print(e.sections)
|
||||
image = [xFirmwareImage('ram_1', ['.ram.start.table', '.ram_image1.text', '.ram_image1.data'], HM_IS_BOOT + HM_IS_SRAM, 0x10000bc8, 0x10070000),
|
||||
xFirmwareImage('ram_2', ['.image2.start.table', '.ram_image2.text', '.ram_image2.rodata', '.ram.data'], HM_IS_OTA + HM_IS_HDRR + HM_IS_SRAM, 0x10006000, 0x10070000),
|
||||
xFirmwareImage('sdram', ['.sdr_text', '.sdr_rodata', '.sdr_data'], HM_IS_OTA + HM_IS_HDRD + HM_IS_SDRAM, 0x30000000, 0x30200000)]
|
||||
|
||||
img_sram_use = 0
|
||||
img_sdram_use = 0
|
||||
|
||||
for s in image:
|
||||
s.load(e.sections)
|
||||
if s.hm & HM_IS_SRAM:
|
||||
img_sram_use += s.size
|
||||
elif s.hm & HM_IS_SDRAM:
|
||||
img_sdram_use += s.size
|
||||
|
||||
for s in image:
|
||||
if s.size:
|
||||
print('Segment at 0x%08x,' % s.addr, 'size 0x%08x' % s.size, '(%s)' % s.fname)
|
||||
s.save(args.outdir, None);
|
||||
s.save(args.outdir, 1);
|
||||
|
||||
if args.ota:
|
||||
fn = args.outdir + 'ota.bin'
|
||||
try:
|
||||
chks = 0
|
||||
with open(fn, "wb") as f:
|
||||
for s in image:
|
||||
if s.hm & HM_IS_OTA:
|
||||
chks = s.save_ota(f, fn, chks)
|
||||
f.write(struct.pack(b'<LLL', 0, 0, chks))
|
||||
f.close()
|
||||
|
||||
except:
|
||||
print('Error: Not write file <%s>!' % fn)
|
||||
sys.exit(-1)
|
||||
|
||||
if args.ram_all:
|
||||
if image[0].size == 0 and image[1].size == 0:
|
||||
print('Error: Ram_all = 0!')
|
||||
sys.exit(-1)
|
||||
fn = args.outdir + 'ram_all.bin'
|
||||
try:
|
||||
with open(fn, "wb") as f:
|
||||
for s in image:
|
||||
s.save_sram(f, fn)
|
||||
f.write(struct.pack(b'<LL', 0, 0))
|
||||
f.close()
|
||||
|
||||
except:
|
||||
print('Error: Not write file <%s>!' % fn)
|
||||
sys.exit(-1)
|
||||
|
||||
print('Images size: SRAM %u bytes, SDRAM %u bytes [%u]' % (img_sram_use, img_sdram_use, img_sram_use + img_sdram_use))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='RtlAImages Utility version %s' % __version__, prog='rtlimage')
|
||||
parser.add_argument('--ram_all', '-r', action='store_true', help='Generate ram_all files')
|
||||
parser.add_argument('--ota', '-a', action='store_true', help='Generate OTA files')
|
||||
parser.add_argument('elffile', type=str, help='Input ELF file',)
|
||||
parser.add_argument('--outdir', '-o', type=str, default='', help='Outpyt directory')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print('RtlAImages Utility version %s' % __version__)
|
||||
if args.elffile is None:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
elf2image(args)
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except FatalError as e:
|
||||
print('\nA fatal error occurred: %s' % e)
|
||||
sys.exit(2)
|
Loading…
Reference in a new issue