Files
DebugTool/source/utl_upgrade.py
何 泽隆 60c5285bcf 修复升级包生成结果与打包工具不一致的问题;
- 调换校验方式与压缩方式字节位置;
 - 调换文件头校验大小端;
2024-08-08 17:06:37 +08:00

518 lines
21 KiB
Python

# 升级包生成脚本
import hashlib
from pathlib import Path
from datetime import datetime
from crc import Calculator, Crc16
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=-1, len_max=-1, **kwargs):
"""
将Intel Hex格式文件转换为Bin格式;
"""
if base_address == -1:
if file_data[8] == '2':
base_address = int(file_data[9: 13], 16) * 16
offset_begin = 16
elif file_data[8] == '4':
base_address = int(file_data[9: 13], 16) * 2**16
offset_begin = 16
else:
base_address = 0
offset_begin = 0
base_address += int(file_data[offset_begin + 3: offset_begin + 7], 16)
base_address &= ~0x0FFF
if len_max == -1:
len_space = 0x10
else:
len_space = len_max
result = bytearray(len_space).replace(b'\x00', b'\xff')
lines = file_data.split('\n')
offset = 0
max_address = 0
for line in lines:
if line[0] == ':':
checksum = sum([int(x, 16) * (15 * (~i & 0x01) + 1) for i, x in enumerate(line[1:])])
if (checksum & 0x00FF) != 0:
raise Exception('Hex file checksum error!')
if line[7:9] == '00':
len_line = int(line[1:3], 16)
address = offset + int(line[3:7], 16) - base_address
data = bytearray.fromhex(line[9:-2])
if 'conv_end' in kwargs.keys():
address *= 2
if kwargs['conv_end']:
for i in range(0, len_line, 2):
t = data[i]
data[i] = data[i+1]
data[i+1] = t
if max_address >= len_space:
if len_max == -1:
result += bytearray((len_line & ~0x0F) + 0x10).replace(b'\x00', b'\xff')
len_space += (len_line & ~0x0F) + 0x10
else:
raise Exception("Bin file Oversize.")
result[address:address+len_line] = data
if (address + len_line) > max_address:
max_address = address + len_line
elif line[7:9] == '01':
break
elif line[7:9] == '02':
offset = int(line[9:-2], 16) * 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 build_header_new(config: dict):
"""
基于配置参数, 生成新版文件信息头;
"""
# 定义文件头
m_file_header = [0xFF] * 184
m_file_header[0:8] = list(b"TOPSCOMM")
m_file_header[8:10] = config['prod_type']
m_file_header[10:22] = config['prog_id'] + [0] * (12 - len(config['prog_id']))
if config['method_compress'] == True:
m_file_header[23] = 0x01
else:
m_file_header[23] = 0x00
if 'crc32' in config.keys():
m_file_header[22] = 0x00
m_file_header[24: 40] = config['crc32'] + [0x00] * 12
elif 'md5' in config.keys():
m_file_header[22] = 0x01
m_file_header[24: 40] = config['md5']
else:
raise Exception("Error, Unknown method verify.")
# 时间戳生成
time_now = datetime.now()
time_stamp = list(map(lambda x: int(x),
time_now.strftime("%Y-%m-%d-%H-%M").split('-')))
time_stamp.insert(1, time_stamp[0] // 0x100)
time_stamp[0] = time_stamp[0] % 0x100
m_file_header[40: 46] = time_stamp
m_file_header[46: 50] = config['file_length']
# Cpu1
m_file_header[50: 54] = [0x00] * 4
m_file_header[54: 70] = [0x00] * 16
# Cpu2
m_file_header[70: 74] = [0x00] * 4
m_file_header[74: 90] = [0x00] * 16
if config['prog_type'] == 'app':
m_file_header[90: 92] = [0x00, 0x00]
elif config['prog_type'] == 'boot':
m_file_header[90: 92] = [0x01, 0x00]
elif config['prog_type'] == 'diff':
m_file_header[90: 92] = [0x02, 0x00]
elif config['prog_type'] == 'font':
m_file_header[90: 92] = [0x03, 0x00]
elif config['prog_type'] == 'config':
m_file_header[90: 92] = [0x04, 0x00]
elif config['prog_type'] == 'data':
m_file_header[90: 92] = [0x05, 0x00]
elif config['prog_type'] == 'test':
m_file_header[90: 92] = [0x06, 0x00]
else:
raise Exception("Error, Unknown Program Type.")
m_file_header[92: 94] = config['area_code']
m_file_header[94: 158] = config['hex_name']
m_file_header[158: 182] = [0x00] * 24
m_file_header = bytearray(m_file_header)
calculator = Calculator(Crc16.MODBUS)
code_crc16 = calculator.checksum(m_file_header[:-2])
m_file_header[182: 184] = [code_crc16 // 0x100, code_crc16 % 0x100]
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 Exception("Header tag oversize. ")
data_encrypt = file_encryption(data_bin)
return data_header, data_bin, data_encrypt
def make_datafile3(file_path: str, config: dict, header_len=184):
""" 升级文件生成函数; 完整文件头 """
file_path = Path(file_path)
file_data = file_path.read_text()
data_bin = file_IntelHex_to_Bin(file_data)
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())
config['hex_name'] = list(file_path.name.encode())[:64]
config['hex_name'] += [0] * (64 - len(config['hex_name']))
data_header = build_header_new(config)
if data_header is None:
raise Exception("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校验, 加密测试 """
bin_offcial = Path(r"D:\WorkingProject\LightStackAdapter\software\lamina_adapter\tools\uart_tool\江苏发货版本\SLCP001_240517_1800_V1.11.bin")
data_offcial = bin_offcial.read_bytes()
byte_data = data_offcial[:182]
crc = data_offcial[182:184]
# data = "54 4F 50 53 43 4F 4D 4D 45 00 53 4C 43 50 30 30 31 00 00 00 00 00 01 00 B6 61 A8 73 BF 82 9E A7 4C 79 F6 BB 94 E2 A5 18 E8 07 05 18 0B 17 18 4C 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6C 61 6D 69 6E 61 5F 61 64 61 70 74 65 72 5F 6D 61 69 6E 02 00 00 00 00 20 10 53 06 00 00 00 00 80 A5 8F 02 00 00 00 00 EC 1F 40 00 00 00 00 00 00 00 00 00 00 00 00 00 7A CA 61 0A FD 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
# byte_data = list(map(lambda x: int(x, 16), data.split(' ')))
# crc = "99 CB"
calculator = Calculator(Crc16.MODBUS)
code_crc16 = calculator.checksum(bytearray(byte_data))
print(f"Result = {hex(code_crc16)}, Offcial Result = {crc}")
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(path_bin):
""" 完整升级包生成测试 """
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': [0x22, 0x04, 0x24], # 升级版本日期
# 'area_code': [], # 省份特征
# 'uptate_str': [], # 升级段描述
# 'device_str': [], # 设备特征描述
# 'hex_name': [], # Hex文件名(自动读取)
# 文件Hex结构信息
# 'flash_addr': 0x3E8020, # 程序起始地址
# 'flash_size': 0x005FC0, # 程序空间大小
}
header, data_b, _ = make_datafile2(path_bin, config, header_len=128)
header_512, data_b_512, _ = make_datafile2(path_bin, config, header_len=512)
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)
file2 = path_bin.parent / (path_bin.stem + '_h512.dat')
file2.write_bytes(header_512 + data_b)
def test4(path_boot, path_main, path_back):
""" 叠光适配器新版升级方案测试 """
config = {
'prod_type': [0x45, 0x00], # 产品类型
'method_compress': False, # 文件压缩
'prog_id': list(b"SLCP001"), # 程序识别号
'prog_type': 'app', # 程序类型
'area_code': [0x00, 0x00], # 地区
}
file_path = Path(path_boot)
file_data = file_path.read_text()
boot_bin = file_IntelHex_to_Bin(file_data, len_max=0x010000)
main_header, main_data_b, main_data_e = make_datafile3(path_main, config)
back_header, back_data_b, back_data_e = make_datafile3(path_back, config)
print("Merge Image generated successfully.")
print(f"Main File:")
print(f"\t header_length={len(main_header)}, bin_length={len(main_data_b)}[{hex(len(main_data_b))}]")
print(f"Back File:")
print(f"\t header_length={len(back_header)}, bin_length={len(back_data_b)}[{hex(len(back_data_b))}]")
# 组装镜像
Image = [0xFF] * 0x100000
offset_image = 0
Image[offset_image: offset_image + len(boot_bin)] = boot_bin
offset_image = 0x010000
Image[offset_image: offset_image + len(main_data_b)] = main_data_b
offset_image = 0x0BC000
Image[offset_image: offset_image + len(back_data_b)] = back_data_b
offset_image = 0x0FC000
Image[offset_image: offset_image + len(main_header)] = main_header
offset_image = 0x0FE000
Image[offset_image: offset_image + len(back_header)] = back_header
return bytearray(Image), main_header, main_data_b, main_data_e, back_header, back_data_b, back_data_e
def GenerateImage_SLCP001_p460(path_boot, path_main, path_back):
""" 叠光适配器-460平台版本 镜像生成 """
config = {
'prod_type': [0x45, 0x00], # 产品类型
'method_compress': False, # 文件压缩
'prog_id': list(b"SLCP001"), # 程序识别号
'prog_type': 'app', # 程序类型
'area_code': [0x00, 0x00], # 地区
}
file_path = Path(path_boot)
file_data = file_path.read_text()
boot_bin = file_IntelHex_to_Bin(file_data, len_max=0x00C000)
main_header, main_data_b, main_data_e = make_datafile3(path_main, config)
back_header, back_data_b, back_data_e = make_datafile3(path_back, config)
print("Merge Image generated successfully.")
print(f"Main File:")
print(f"\t header_length={len(main_header)}, bin_length={len(main_data_b)}[{hex(len(main_data_b))}]")
print(f"Back File:")
print(f"\t header_length={len(back_header)}, bin_length={len(back_data_b)}[{hex(len(back_data_b))}]")
# 组装镜像
Image = [0xFF] * 0x058000
offset_image = 0
Image[offset_image: offset_image + len(boot_bin)] = boot_bin
offset_image = 0x00C000
Image[offset_image: offset_image + len(main_data_b)] = main_data_b
offset_image = 0x030000
Image[offset_image: offset_image + len(back_data_b)] = back_data_b
offset_image = 0x054000
Image[offset_image: offset_image + len(main_header)] = main_header
offset_image = 0x056000
Image[offset_image: offset_image + len(back_header)] = back_header
return bytearray(Image), main_header, main_data_b, main_data_e, back_header, back_data_b, back_data_e
def GeneratePackage_SLCP001_p460(path_hex):
""" 叠光适配器-460平台版本 生成升级包 """
config = {
'prod_type': [0x45, 0x00], # 产品类型
'method_compress': False, # 文件压缩
'prog_id': list(b"SLCP001"), # 程序识别号
'prog_type': 'app', # 程序类型
'area_code': [0x00, 0x00], # 地区
}
main_header, main_data_b, main_data_e = make_datafile3(path_hex, config)
print("Merge Image generated successfully.")
print(f"File Info:")
print(f"\t header_length={len(main_header)}, bin_length={len(main_data_b)}[{hex(len(main_data_b))}]")
# 组装镜像
Image = [0xFF] * (len(main_header) + len(main_data_e))
offset_image = 0
Image[offset_image: offset_image + len(main_header)] = main_header
offset_image += len(main_header)
Image[offset_image: offset_image + len(main_data_e)] = main_data_e
return bytearray(Image), main_data_b
def Process1():
""" 镜像生成流程 """
root = Path(r"test\p460")
result = Path(r"test\p460\result")
# 正常启动镜像
hex_boot = root / r"bootloader.hex"
hex_main = root / r"lamina_adapter.hex"
hex_back = root / r"lamina_adapter_back.hex"
file_image = result / f'{hex_main.stem[:-6]}_ROM.bin'
file_main_header = result / 'SLCP001_header_main.bin'
file_back_header = result / 'SLCP001_header_back.bin'
data_bins = GenerateImage_SLCP001_p460(hex_boot, hex_main, hex_back)
file_image.write_bytes(data_bins[0].copy())
file_main_header.write_bytes(data_bins[1].copy())
file_back_header.write_bytes(data_bins[4].copy())
# 异常镜像-主分区md5错误
file_image1 = result / f'{file_image.stem}_b1.bin'
data_image = data_bins[0].copy()
data_image[0x054018: 0x05401A] = [0x00, 0x01]
file_image1.write_bytes(data_image)
# 异常镜像-备份分区md5错误
file_image2 = result / f'{file_image.stem}_b2.bin'
data_image = data_bins[0].copy()
data_image[0x056018: 0x05601A] = [0x00, 0x01]
file_image2.write_bytes(data_image)
# 异常镜像-双分区md5错误
file_image3 = result / f'{file_image.stem}_b3.bin'
data_image = data_bins[0].copy()
data_image[0x054018: 0x05401A] = [0x00, 0x01]
data_image[0x056018: 0x05601A] = [0x00, 0x01]
file_image3.write_bytes(data_image)
def Process2():
""" 升级包生成流程 """
root = Path(r"test\p460")
result = Path(r"test\p460\result")
hex_main = root / r"lamina_adapter_t1.hex"
file_package = result / f'{hex_main.stem}.dat'
file_bin = result / f'{hex_main.stem}.bin'
data_package, data_bins = GeneratePackage_SLCP001_p460(hex_main)
file_package.write_bytes(data_package)
file_bin.write_bytes(data_bins)
if __name__ == "__main__":
# path_bin = Path("F:\\Work\\FPGA\\Test\\Vivado\\test_update\\test_update.vitis\\upgrade_system\\Debug\\sd_card\\BOOT.BIN")
# test3(path_bin)
# test2()
if 0:
bin_back = Path(r"D:\WorkingProject\LightStackAdapter\software\lamina_adapter\tools\upgrade\lamina_adapter_back.bin")
bin_main = Path(r"D:\WorkingProject\LightStackAdapter\software\lamina_adapter\tools\upgrade\lamina_adapter_main.bin")
main_header1 = bin_main.read_bytes()[:184]
main_data_e1 = bin_main.read_bytes()[184:]
back_header1 = bin_back.read_bytes()[:184]
back_data_e1 = bin_back.read_bytes()[184:]
Process1()