|
|
|
@ -41,6 +41,8 @@ class ImgHeader():
|
|
|
|
|
class Image():
|
|
|
|
|
type = None # fw_img / kernel / rootfs
|
|
|
|
|
ostype = None # 'stock', 'openwrt', 'padavan', 'pandorabox', etc
|
|
|
|
|
into_ubi = False
|
|
|
|
|
initrd = False
|
|
|
|
|
hdr = ImgHeader()
|
|
|
|
|
addr = None
|
|
|
|
|
addr2 = None # for kernel_stok/kernel_dup
|
|
|
|
@ -150,12 +152,15 @@ class XqFlash():
|
|
|
|
|
self.img_stock_names = { }
|
|
|
|
|
print('Parse all images...')
|
|
|
|
|
for img in self.imglist:
|
|
|
|
|
self.current_image_fn = img.fn
|
|
|
|
|
self.current_image_pos = 0
|
|
|
|
|
with open(img.fn, "rb") as file:
|
|
|
|
|
image = file.read()
|
|
|
|
|
if img.type == 'stock':
|
|
|
|
|
self.parse_stock_image(image)
|
|
|
|
|
else:
|
|
|
|
|
self.parse_image(image, None)
|
|
|
|
|
self.current_image_fn = None
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def init_image(self, image, data, err_msg):
|
|
|
|
@ -190,12 +195,24 @@ class XqFlash():
|
|
|
|
|
if img_name:
|
|
|
|
|
self.img_stock_names[img_name] = len(image)
|
|
|
|
|
if image[:8] == UBIv1_MAGIC:
|
|
|
|
|
hr = self.parse_ubifs(image)
|
|
|
|
|
print(f'parse_ubifs = {hr}')
|
|
|
|
|
if hr >= 2:
|
|
|
|
|
self.init_image(self.fw_img, image, 'Incorrect image! (401)')
|
|
|
|
|
ubivol = self.parse_ubifs(image)
|
|
|
|
|
print(f'parse_ubifs = {len(ubivol)}')
|
|
|
|
|
kk = 0
|
|
|
|
|
if 'kernel' in ubivol:
|
|
|
|
|
self.kernel.into_ubi = True
|
|
|
|
|
kk = self.parse_fit(ubivol['kernel'], footer = False)
|
|
|
|
|
if kk <= 0:
|
|
|
|
|
die('FIT: Incorrect image! (401)')
|
|
|
|
|
hr += kk
|
|
|
|
|
if 'rootfs' in ubivol:
|
|
|
|
|
self.init_image(self.rootfs, ubivol['rootfs'], 'Incorrect image! (402)')
|
|
|
|
|
self.rootfs.into_ubi = True
|
|
|
|
|
hr += 1
|
|
|
|
|
if 'kernel' in ubivol and ('rootfs' in ubivol or kk == 2):
|
|
|
|
|
self.init_image(self.fw_img, image, 'Incorrect image! (403)')
|
|
|
|
|
if img_name:
|
|
|
|
|
self.img_stock_names[img_name] = len(image)
|
|
|
|
|
self.save_all_images(req_cmd = False, prefix = "_ubi_")
|
|
|
|
|
return hr
|
|
|
|
|
|
|
|
|
|
def parse_stock_image(self, image):
|
|
|
|
@ -217,6 +234,7 @@ class XqFlash():
|
|
|
|
|
img.data = data[img.offset+hdr_size:img.offset+hdr_size+img.size]
|
|
|
|
|
if len(img.data) != img.size:
|
|
|
|
|
die('Incorrect stock image! (4)')
|
|
|
|
|
self.current_image_pos = img.offset + hdr_size
|
|
|
|
|
#print('offset = {} header = {}'.format("%08X" % (img.offset + hdr_size), img.data[:4]))
|
|
|
|
|
imglst.append(img)
|
|
|
|
|
|
|
|
|
@ -343,6 +361,8 @@ class XqFlash():
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
def parse_fit(self, image, offset = 0, footer = True):
|
|
|
|
|
kernel = self.kernel
|
|
|
|
|
rootfs = self.rootfs
|
|
|
|
|
data = image
|
|
|
|
|
if image is None:
|
|
|
|
|
data = self.kernel.data
|
|
|
|
@ -359,7 +379,6 @@ class XqFlash():
|
|
|
|
|
self.init_image(self.kernel, data, 'FIT: Found second "kernel" section!')
|
|
|
|
|
else:
|
|
|
|
|
self.kernel.data = data
|
|
|
|
|
kernel = self.kernel
|
|
|
|
|
kernel.ostype = None
|
|
|
|
|
print('FIT size = 0x%X (%d KiB)' % (fit_size, fit_size // 1024))
|
|
|
|
|
fit_dt = fdt.parse_dtb(kernel.data)
|
|
|
|
@ -375,6 +394,18 @@ class XqFlash():
|
|
|
|
|
kernel.ostype = 'openwrt'
|
|
|
|
|
if not kernel.ostype:
|
|
|
|
|
die('FIT: Currently supported only OpenWrt FIT images!')
|
|
|
|
|
if kernel.into_ubi:
|
|
|
|
|
x1 = kernel.data.find(b'ARM64 OpenWrt xiaomi', 1*1024*1024)
|
|
|
|
|
if x1 > 0:
|
|
|
|
|
iname = extract_str(kernel.data, x1, maxlen = 256)
|
|
|
|
|
print(f'FIT: Found rootfs image: "{iname}"')
|
|
|
|
|
self.init_image(rootfs, kernel.data[x1:], 'FIT: Found second "rootfs" section!')
|
|
|
|
|
if ' initrd' in iname:
|
|
|
|
|
kernel.initrd = True
|
|
|
|
|
rootfs.initrd = True
|
|
|
|
|
if kernel.into_ubi:
|
|
|
|
|
rootfs.into_ubi = True
|
|
|
|
|
return 2
|
|
|
|
|
if footer:
|
|
|
|
|
hr = self.parse_footer(image, offset + fit_size)
|
|
|
|
|
if hr >= 1:
|
|
|
|
@ -408,10 +439,53 @@ class XqFlash():
|
|
|
|
|
self.rootfs.data = rootfs_data
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
def parse_ubifs(self, image, init = True):
|
|
|
|
|
hr = 1
|
|
|
|
|
self.init_image(self.rootfs, image, 'UBIFS: Found second "rootfs" section!')
|
|
|
|
|
return hr
|
|
|
|
|
def parse_ubifs(self, ubifs_image, init = True):
|
|
|
|
|
from ubireader.ubi import ubi
|
|
|
|
|
from ubireader.ubi import ubi_base
|
|
|
|
|
from ubireader.ubi_io import ubi_file
|
|
|
|
|
from ubireader import settings
|
|
|
|
|
from ubireader.ubi.defines import UBI_EC_HDR_MAGIC
|
|
|
|
|
from ubireader.ubifs.defines import UBIFS_NODE_MAGIC
|
|
|
|
|
from ubireader.utils import guess_filetype, guess_start_offset, guess_leb_size, guess_peb_size
|
|
|
|
|
|
|
|
|
|
settings.logging_on = False
|
|
|
|
|
settings.logging_on_verbose = False
|
|
|
|
|
settings.warn_only_block_read_errors = False
|
|
|
|
|
settings.ignore_block_header_errors = False
|
|
|
|
|
settings.uboot_fix = False
|
|
|
|
|
path = self.current_image_fn
|
|
|
|
|
start_offset = self.current_image_pos
|
|
|
|
|
filetype = guess_filetype(path, start_offset)
|
|
|
|
|
print('UBI: filetype:', filetype)
|
|
|
|
|
if filetype != UBI_EC_HDR_MAGIC:
|
|
|
|
|
die('UBI: File does not look like UBI data.')
|
|
|
|
|
block_size = guess_peb_size(path)
|
|
|
|
|
if not block_size:
|
|
|
|
|
die('UBI: Block size could not be determined.')
|
|
|
|
|
ufile_obj = ubi_file(path, block_size, start_offset)
|
|
|
|
|
#ubi_obj = ubi_base(ufile_obj)
|
|
|
|
|
ubi_obj = ubi(ufile_obj)
|
|
|
|
|
print('UBI: Decoding UBIFS...')
|
|
|
|
|
kernel_volume = None
|
|
|
|
|
rootfs_volume = None
|
|
|
|
|
for image in ubi_obj.images:
|
|
|
|
|
for volume in image.volumes:
|
|
|
|
|
data = b""
|
|
|
|
|
vol = image.volumes[volume]
|
|
|
|
|
for block in vol.reader(ubi_obj):
|
|
|
|
|
data += block
|
|
|
|
|
if volume == 'kernel' and len(data) > 1024:
|
|
|
|
|
kernel_volume = data
|
|
|
|
|
if volume == 'rootfs' and len(data) > 1024:
|
|
|
|
|
rootfs_volume = data
|
|
|
|
|
print(f'UBI: volume: "{volume}" \t size: {len(data)} ')
|
|
|
|
|
ufile_obj.close()
|
|
|
|
|
out = { }
|
|
|
|
|
if kernel_volume:
|
|
|
|
|
out['kernel'] = kernel_volume
|
|
|
|
|
if rootfs_volume:
|
|
|
|
|
out['rootfs'] = rootfs_volume
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
|
def unpack_kernel(self):
|
|
|
|
|
kernel = self.kernel
|
|
|
|
@ -592,7 +666,13 @@ class XqFlash():
|
|
|
|
|
ubi1_num = dev.get_part_num('ubi1')
|
|
|
|
|
if ubi0_num > 0 and ubi1_num > 0 and kernel_num < 0:
|
|
|
|
|
self.install_method = 400
|
|
|
|
|
die("Unsupported install method 400")
|
|
|
|
|
if not fw_img.data or not kernel.data or not rootfs.data:
|
|
|
|
|
die('Cannot firmware image! (400)')
|
|
|
|
|
if not kernel.into_ubi:
|
|
|
|
|
die('Kernel image must be into UBIFS (400)')
|
|
|
|
|
if kernel.ostype == 'openwrt':
|
|
|
|
|
if not kernel.initrd:
|
|
|
|
|
die('OpenWRT: Supported only InitRamFS images (400)')
|
|
|
|
|
|
|
|
|
|
print(f'install_method = {self.install_method}')
|
|
|
|
|
if self.install_method <= 0:
|
|
|
|
@ -677,6 +757,22 @@ class XqFlash():
|
|
|
|
|
if fw_part['ro']:
|
|
|
|
|
die(f'Target partition "{fw_img.partname}" has readonly flag')
|
|
|
|
|
|
|
|
|
|
if self.install_method == 400:
|
|
|
|
|
fw_img.partname = 'ubi'
|
|
|
|
|
if self.img_stock:
|
|
|
|
|
if self.install_fw_num == 1:
|
|
|
|
|
fw_img.partname = 'ubi1'
|
|
|
|
|
|
|
|
|
|
fw_part = dev.get_part(fw_img.partname)
|
|
|
|
|
fw_img.addr = fw_part['addr']
|
|
|
|
|
fw_img.cmd = 'mtd -e "{part}" write "{bin}" "{part}"'.format(part=fw_img.partname, bin=fw_img.fn_remote)
|
|
|
|
|
kernel.cmd = None
|
|
|
|
|
rootfs.cmd = None
|
|
|
|
|
if 'ro' not in fw_part:
|
|
|
|
|
die(f'Cannot get readonly flag for partition "{fw_img.partname}"')
|
|
|
|
|
if fw_part['ro']:
|
|
|
|
|
die(f'Target partition "{fw_img.partname}" has readonly flag')
|
|
|
|
|
|
|
|
|
|
self.save_all_images(req_cmd = True, prefix = "")
|
|
|
|
|
|
|
|
|
|
def save_image_to_disk(self, image, req_cmd = True, prefix = ""):
|
|
|
|
|