diff --git a/install_fw.py b/install_fw.py index 1fc425a..3cac16e 100644 --- a/install_fw.py +++ b/install_fw.py @@ -148,7 +148,17 @@ class XqFlash(): self.cpuarch = self.dev.info.cpu_arch if self.cpuarch not in 'mips armv7 arm64': die("Currently support only MIPS, ARMv7, ARM64 arch!") - + ''' + if True: + for part in self.dev.partlist: + if part['name'] == 'firmware': + part['name'] = 'ubi' + if part['name'] == 'firmware1': + part['name'] = 'ubi1' + if part['name'] == 'kernel': + part['name'] = '_kernel' + print('=== device partlist patched ===') + ''' self.img_stock_names = { } print('Parse all images...') for img in self.imglist: @@ -212,7 +222,7 @@ class XqFlash(): 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_") + #self.save_all_images(req_cmd = False, prefix = "_ubi_") return hr def parse_stock_image(self, image): @@ -360,6 +370,55 @@ class XqFlash(): return 2 return 1 + def get_fdt_node(self, dt, path): + plist = [ path ] + if '*' in path: + plist = [ ] + plist.append(path.replace('*', '-')) + plist.append(path.replace('*', '@')) + for npath in plist: + try: + node = dt.get_node(npath) + return node + except ValueError: + pass + return None + + def get_fdt_node_by_name(self, dt, name, compatible = None): + res = [ ] + for path, nodes, props in dt.walk(): + nodename = os.path.basename(path) + if nodename == name: + if compatible: + try: + compat = dt.get_property('compatible', path) + except ValueError: + continue # go to next node + if compat.value != compatible: + continue # go to next node + res.append(path) + return res + + def get_fdt_part_list(self, dt, partlist): + res = [ ] + if isinstance(partlist, str): + partlist = dt.get_node(partlist) + for node in partlist.nodes: + name = node.get_property('label').value + addr = node.get_property('reg')[0] + size = node.get_property('reg')[1] + readonly = False + if node.get_property('read-only'): + readonly = True + res.append( { 'addr': addr, 'size': size, 'name': name, 'ro': readonly } ) + return res + + def get_dtb_part_info(self, partlist, name): + for i, part in enumerate(partlist): + if part['name'] == name: + return part + return None + def parse_fit(self, image, offset = 0, footer = True): kernel = self.kernel rootfs = self.rootfs @@ -382,30 +441,69 @@ class XqFlash(): kernel.ostype = None print('FIT size = 0x%X (%d KiB)' % (fit_size, fit_size // 1024)) fit_dt = fdt.parse_dtb(kernel.data) + #print(fit_dt.info(props = True)) #if fit_dt.root.nodes[0]._name != 'images': # die('FIT: Incorrect image (4)') fit_name = fit_dt.get_property('description').value print(f'FIT: name = "{fit_name}"') + self.fit_dt = fit_dt + fdt1 = self.get_fdt_node(fit_dt, '/images/fdt*1') + print('FDT: desc = "{}"'.format(fdt1.get_property('description').value)) + print('FDT: type = "{}"'.format(fdt1.get_property('type').value)) + print('FDT: arch = "{}"'.format(fdt1.get_property('arch').value)) + if fdt1.get_property('type').value != 'flat_dt': + die('FIT: Incorrect image (6)') + if fdt1.get_property('compression').value != 'none': + die('FIT: Incorrect image (7)') + kernel.hdr.arch = fdt1.get_property('arch').value + krn1 = self.get_fdt_node(fit_dt, '/images/kernel*1') + print('KRN: desc = "{}"'.format(krn1.get_property('description').value)) + print('KRN: type = "{}"'.format(krn1.get_property('type').value)) + print('KRN: arch = "{}"'.format(krn1.get_property('arch').value)) + print('KRN: compression = "{}"'.format(krn1.get_property('compression').value)) + print(f'KRN: len(data) = {len(krn1.get_property("data"))} bytes') + + krn_dt_data = fdt1.get_property('data').data + dt = fdt.parse_dtb(krn_dt_data) + self.krn_dt = dt + dt_tree = dt.info(props = True) + #with open('dt_tree.txt', "w") as file: + # file.write(dt_tree) + dt_compat = dt.get_property('compatible').value + print(f'FDT: compatible = "{dt_compat}"') + dt_model = dt.get_property('model').value + print(f'FDT: model = "{dt_model}"') + + dt_part = self.get_fdt_node_by_name(dt, 'partitions', 'fixed-partitions') + print(f'FDT: dt_part: {dt_part}') + kernel.fit = True if self.img_stock: - kernel.ostype = 'stock' # aka OpenWRT + kernel.ostype = 'stock' else: if 'OpenWrt FIT' in fit_name: 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 + + rootfs1 = self.get_fdt_node(fit_dt, '/images/rootfs*1') + if rootfs1: + die('FIT: Founded "rootfs-1" node. Not supported!') + + initrd1 = self.get_fdt_node(fit_dt, '/images/initrd*1') + if initrd1: + print('FIT: Founded "initrd-1" node') + iname = initrd1.get_property('description').value + print(f'FIT: initrd image name: "{iname}"') + if self.img_stock: + die('FIT: Error (4566)') + initrd1_data = initrd1.get_property('data') + self.init_image(rootfs, initrd1.get_property('data').data, 'FIT: Found second "rootfs" section!') + 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: @@ -569,7 +667,23 @@ class XqFlash(): dtb = get_dtb(kernel.data2, 0) if not dtb: die("Can't found FDT (flattened device tree)") - kernel_part = get_dtb_part_info(dtb, "kernel") + dt = fdt.parse_dtb(dtb) + #print(dt.info(props = True)) + dt_compat = dt.get_property('compatible').value + print(f'FDT: compatible = "{dt_compat}"') + dt_model = dt.get_property('model').value + print(f'FDT: model = "{dt_model}"') + self.dt = dt + dt_part = self.get_fdt_node_by_name(dt, 'partitions', 'fixed-partitions') + print(f'FDT: dt_part: {dt_part}') + if len(dt_part) == 0: + die("Cannot found fixed-partitions node into FDT") + if len(dt_part) > 1: + die("Several nodes were found with fixed-partition info") + dt_part = dt_part[0] + partlist = self.get_fdt_part_list(dt, dt_part) + #print(partlist) + kernel_part = self.get_dtb_part_info(partlist, "kernel") if not kernel_part: die('Cannot found "kernel" partition in DTB!') print('part kernel = 0x%X (size: 0x%X)' % (kernel_part['addr'], kernel_part['size'])) @@ -577,9 +691,9 @@ class XqFlash(): part = dev.get_part_by_addr(kernel.addr) if not part: die("Can't support flashing kernel to addr 0x%X" % kernel.addr) - kernel2_part = get_dtb_part_info(dtb, "kernel_dup") + kernel2_part = self.get_dtb_part_info(partlist, "kernel_dup") if not kernel2_part: - kernel2_part = get_dtb_part_info(dtb, "kernel_stock") + kernel2_part = self.get_dtb_part_info(partlist, "kernel_stock") if not kernel2_part: die('Cannot found "kernel_dup"/"kernel_stock" partition in DTB!') print('part kernel2 = 0x%X (size: 0x%X)' % (kernel2_part['addr'], kernel2_part['size'])) @@ -587,7 +701,7 @@ class XqFlash(): part = dev.get_part_by_addr(kernel.addr2) if not part: die("Can't support flashing kernel to addr 0x%X" % kernel.addr2) - ubi_part = get_dtb_part_info(dtb, "ubi") + ubi_part = self.get_dtb_part_info(partlist, "ubi") if not ubi_part: die('Cannot found "ubi" partition in DTB!') print('part ubi = 0x%X (size: 0x%X)' % (ubi_part['addr'], ubi_part['size']))