# 升级包生成脚本 import hashlib from pathlib import Path from utl import conv_int_to_array, trans_list_to_str Header_Tag = { 'file_type': [0x00, 2], # 0 - 文件类型; 2byte 'file_version': [0x01, 2], # 1 - 文件版本; 2byte 'file_length': [0x02, 4], # 2 - 文件长度; 4byte 'md5': [0x03, 16], # 3 - 文件MD5; 16byte 'encrypt': [0x04, 1], # 4 - 加密算法; 1byte 'update_type': [0x05, 1], # 5 - 升级文件类别; 1byte 'update_spec': [0x06, 4], # 6 - 升级特征字; 4byte 'update_verison': [0x07, 4], # 7 - 升级版本号; 4byte 'update_date': [0x08, 3], # 8 - 升级版本日期; 3byte 'area_code': [0x09, 4], # 9 - 省份特征; 4byte 'uptate_str': [0x0A, -1, 64], # 10 - 升级段描述; less than 64byte 'device_str': [0x0D, -1, 64], # 13 - 设备特征描述; less than 64byte 'hex_name': [0xFF, -1, 80], # 255 - Hex文件名; less than 80byte } def file_IntelHex_to_Bin(file_data, base_address, len_max=0xC000, conv_end=False): """ 将Intel Hex格式文件转换为Bin格式; """ result = bytearray(len_max).replace(b'\x00', b'\xff') lines = file_data.split('\n') offset = 0 max_address = 0 for line in lines: if max_address >= len_max: raise("Bin file Oversize.") if line[0] == ':': checksum = sum([int(x, 16) * (15 * (~i & 0x01) + 1) for i, x in enumerate(line[1:])]) if (checksum & 0x00FF) != 0: raise('Hex file checksum error!') if line[7:9] == '00': len = int(line[1:3], 16) address = offset + int(line[3:7], 16) - base_address address *= 2 data = bytearray.fromhex(line[9:-2]) if conv_end: for i in range(0, len, 2): t = data[i] data[i] = data[i+1] data[i+1] = t result[address:address+len] = data if (address + len) > max_address: max_address = address + len elif line[7:9] == '01': break elif line[7:9] == '02': offset = int(line[9:-2], 16) * 2**16 elif line[7:9] == '03': pass elif line[7:9] == '04': # 拓展地址偏移 offset = int(line[9:-2], 16) * 2**16 elif line[7:9] == '05': pass return result[:max_address] def file_encryption(buffer): """ 文件加密算法 """ pwd_idx = 0 pwd = b'moc.mmocspot.www' pwd = list(map(lambda x: (x - 0x30 + 0x100) % 0x100, pwd)) result = bytearray(len(buffer)) for i in range(len(buffer)): k = i k |= i >> 8 k |= i >> 16 k |= i >> 24 result[i] = buffer[i] ^ pwd[pwd_idx] ^ (k & 0xFF) pwd_idx = (pwd_idx + 1) % len(pwd) return result def build_header(config: dict, len_max=512): """ 基于配置参数, 生成文件信息头; """ # 定义文件头 m_file_header = bytearray(len_max) header_len = 11 tag_num = 0 for tag, value in config.items(): if tag in Header_Tag.keys(): if Header_Tag[tag][1] == -1: tag_len = min(len(value), Header_Tag[tag][2]) else: tag_len = Header_Tag[tag][1] tag_date = [Header_Tag[tag][0], tag_len] + value[:tag_len] m_file_header[header_len: header_len + tag_len + 2] = bytearray(tag_date) tag_num += 1 header_len += 2 + tag_len m_file_header[0:8] = b"TOPSCOMM" m_file_header[8] = ((header_len - 10) % 0x100) m_file_header[9] = ((header_len - 10) // 0x100) m_file_header[10] = tag_num m_file_header[header_len] = sum(m_file_header[:header_len]) % 0x100 m_file_header[header_len+1] = sum(m_file_header[:header_len]) // 0x100 if header_len+2 > len_max: return None else: return m_file_header def build_header_lite(config: dict): """ 基于配置参数, 生成文件信息头; """ # 定义文件头 m_file_header = bytearray(64) m_file_header[0:4] = bytearray(config["update_verison"]) m_file_header[4:8] = bytearray(config["file_length"]) m_file_header[8:24] = bytearray(config["md5"]) return m_file_header def make_datafile(file_path: str, config: dict, header_len=512): """ 升级文件生成函数; 完整文件头, TI-hex转换 """ file_path = Path(file_path) file_data = file_path.read_text() data_bin = file_IntelHex_to_Bin(file_data, config['flash_addr'], len_max=config['flash_size'] * 2) md5_ctx = hashlib.md5() md5_ctx.update(data_bin) config['file_length'] = conv_int_to_array(len(data_bin)) config["md5"] = list(md5_ctx.digest()) if header_len > 256: config['hex_name'] = list(file_path.name.encode())[:80] data_header = build_header(config, header_len) if data_header is None: raise ("header tag oversize! ") data_encrypt = file_encryption(data_bin) return data_header, data_bin, data_encrypt def make_datafile1(file_path: str, config: dict, header_len=512): """ 升级文件生成函数; 简易文件头, TI-Hex转换 """ file_path = Path(file_path) file_data = file_path.read_text() data_bin = file_IntelHex_to_Bin(file_data, config['flash_addr'], len_max=config['flash_size'] * 2) md5_ctx = hashlib.md5() md5_ctx.update(data_bin) config['file_length'] = conv_int_to_array(len(data_bin), True) config["md5"] = list(md5_ctx.digest()) if header_len > 256: config['hex_name'] = list(file_path.name.encode())[:80] data_header = build_header_lite(config) data_encrypt = file_encryption(data_bin) return data_header, data_bin, data_encrypt def make_datafile2(file_path: str, config: dict, header_len=512): """ 升级文件生成函数 """ file_path = Path(file_path) data_bin = file_path.read_bytes() md5_ctx = hashlib.md5() md5_ctx.update(data_bin) config['file_length'] = conv_int_to_array(len(data_bin)) config["md5"] = list(md5_ctx.digest()) if header_len > 256: config['hex_name'] = list(file_path.name.encode())[:80] data_header = build_header(config, header_len) if data_header is None: raise("Header tag oversize. ") data_encrypt = file_encryption(data_bin) return data_header, data_bin, data_encrypt def test1(fp): """ bin文件生成测试 """ file1 = Path(fp) path1 = file1.parent / (file1.stem + ".bin") data1 = file1.read_text() data2 = file_IntelHex_to_Bin(data1, 0x3F0100) path1.write_bytes(data2) pass def test2(): """ md5校验, 加密测试 """ data_bin = '123456 123456 '.encode() md5_ctx = hashlib.md5() md5_ctx.update(data_bin) hash = md5_ctx.hexdigest() buffer1 = data_bin[:] buffer1_en = file_encryption(buffer1) buffer2 = buffer1_en[:6] + buffer1[6:] buffer2_en = file_encryption(buffer2) pass def test3(): """ 完整升级包生成测试 """ config = { 'file_type': [0x10, 0x01], # Xilinx-Demo 自机升级文件 'file_version': [0x00, 0x00], # 文件版本-00 用于兼容文件格式升级 # 'file_length': [], # 文件长度(自动生成) # 'md5': [], # 文件MD5(自动生成) 'encrypt': [0x01], # 默认加密算法 'update_type': [0x01], # APP升级 'update_spec': [0x00, 0x00, 0x00, 0x00], # 升级特征字 'update_verison': [0x02, 0x00, 0x00, 0x01], # 升级版本号 'update_date': [0x19, 0x04, 0x24], # 升级版本日期 # 'area_code': [], # 省份特征 # 'uptate_str': [], # 升级段描述 # 'device_str': [], # 设备特征描述 # 'hex_name': [], # Hex文件名(自动读取) # 文件结构信息 'flash_addr': 0x3E8020, # 程序文件起始地址 'flash_size': 0x005FC0, # 程序文件大小 } path_bin = Path("F:\\Work\\FPGA\\Test\\Vivado\\test_update\\test_update.vitis\\upgrade_system\\Debug\\sd_card\\BOOT.BIN") header, data_b, _ = make_datafile2(path_bin, config, header_len=128) print("Upgrade file generated successfully.") print(f"\t header_length={len(header)}, bin_length={len(data_b)}[{hex(len(data_b))}]") print(f"\t file md5: {trans_list_to_str(config['md5'])}") file1 = path_bin.parent / (path_bin.stem + '.dat') file1.write_bytes(header + data_b) if __name__ == "__main__": # test1() # test2() test3() pass