1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4# Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16""" 17Description : Generate the update.bin chunk info data 18""" 19import os 20import struct 21import hashlib 22import enum 23import tempfile 24import zipfile 25from log_exception import UPDATE_LOGGER 26from utils import OptionsManager 27from utils import ZIP_EVENT 28 29CHUNK_LIST_COUNT_SIZE = 4 30CHUNK_LIST_SIZE = 8 31CHUNK_RESERVE = 244 32UPGRADE_CHUNKINFO_SIZE = 256 33 34CHUNK_TLV_FMT = "<HI" 35CHUNK_INFO_FMT = "<HI" 36CHUNK_DATA_HEADER_FMT = "<HI" 37CHUNK_SIGN_HEADER_FMT = '<HI' 38CHUNK_DATA_PARTITION_FMT = '<HH' 39CHUNK_DATA_CMD_FMT = '<HH' 40CHUNK_DATA_DATA_FMT = '<HI' 41CHUNK_HASH_HEADER_FMT = '<3H' 42CHUNK_HASH_PARTITION_FMT = '<HH' 43CHUNK_HASH_VALUE_FMT = '<HH' 44CHUNK_LARGE_PARTITION_FMT = '<HH' 45CHUNK_SIGN_PARTITON_FMT = "<HH" 46CHUNK_SIGN_VALUE_FMT = '<HI' 47 48 49OPTIONS_MANAGER = OptionsManager() 50 51 52def get_file_sha256(update_package): 53 """ 54 Get the SHA256 value of the package file 55 """ 56 sha256obj = hashlib.sha256() 57 maxbuf = 8192 58 with open(update_package, 'rb') as package_file: 59 while True: 60 buf = package_file.read(maxbuf) 61 if not buf: 62 break 63 sha256obj.update(buf) 64 hash_value_hex = sha256obj.hexdigest() 65 hash_value = sha256obj.digest() 66 return str(hash_value_hex).upper() 67 68 69def get_chunk_sha256(chunk): 70 """ 71 Get the SHA256 value of the chunk 72 """ 73 sha256obj = hashlib.sha256() 74 sha256obj.update(chunk) 75 hash_value_hex = sha256obj.hexdigest() 76 hash_value = sha256obj.digest() 77 return str(hash_value_hex).upper() 78 79 80class CreateChunk(object): 81 """ 82 Create the image chunk data 83 """ 84 def __init__(self, num, count): 85 self.chunkinfo_tlv_type = 0x10 86 self.chunkdata_tlv_type = 0x11 87 self.chunkdata_partition_tlv_type = 0x12 88 self.chunkdata_cmd_tlv_type = 0x13 89 self.chunkdata_value_tlv_type = 0x14 90 self.chunkhash_tlv_type = 0x15 91 self.chunkhash_info_tlv_type = 0x16 92 self.chunkhash_partition_tlv_type = 0x17 93 self.chunkhash_value_tlve_type = 0x18 94 self.chunkimage_large_tlv_type = 0x19 95 self.chunksign_value_tlv_type = 0x1a 96 97 self.chunk_list_count = count 98 self.chunk_list_num = num 99 self.chunk_list_length = 0 100 self.chunkinfo_value = bytes() 101 self.pkg_chunk = bytes() 102 self.hash_chunk = bytes() 103 self.sign_chunk = bytes() 104 self.chunkdata_value = bytes() 105 self.chunk_list = [] 106 self.hash_list = [] 107 self.sign_list = [] 108 self.all_image_hash_data = [] 109 self.write_chunk_hashdata = bytes() 110 self.signdata = bytes() 111 112 113 def write_chunkinfo(self, package_file, startoffset): 114 UPDATE_LOGGER.print_log("write_chunkinfo Startoffset %s!"\ 115 % startoffset) 116 try: 117 chunkinfo_tlv = struct.pack(CHUNK_TLV_FMT, self.chunkinfo_tlv_type, UPGRADE_CHUNKINFO_SIZE) 118 chunkinfo_header = struct.pack(CHUNK_INFO_FMT, self.chunk_list_count, self.chunk_list_num) 119 except struct.error: 120 UPDATE_LOGGER.print_log("Pack fail!", log_type=UPDATE_LOGGER.ERROR_LOG) 121 raise RuntimeError 122 123 # write chunkinfo 124 self.chunkinfo_value = chunkinfo_tlv + chunkinfo_header 125 package_file.seek(startoffset) 126 package_file.write(self.chunkinfo_value) 127 return True 128 129 def write_chunklist(self, image, package_file, startoffset): 130 UPDATE_LOGGER.print_log("write pkg chunklist StartOffset:%s"\ 131 % startoffset) 132 try: 133 chunk_count = 0 134 patch_index = 0 135 new_index = 0 136 for chunk in OPTIONS_MANAGER.image_transfer_dict_contents[image].splitlines()[4:]: 137 chunk_count += 1 138 partiton_info = image 139 cmd_info = chunk 140 141 # Step 1: Pack partition name 142 partition_tlv = struct.pack(CHUNK_DATA_PARTITION_FMT, self.chunkdata_partition_tlv_type, len(partiton_info)) + partiton_info.encode('utf-8') 143 print(f"Packed partition TLV{chunk_count}: {partition_tlv}") 144 # Write to binary file 145 package_file.seek(startoffset) 146 package_file.write(partition_tlv) 147 startoffset += len(partition_tlv) 148 149 # Step 2: Pack command info 150 cmd_len = len(cmd_info) 151 cmd_tlv = struct.pack(CHUNK_DATA_CMD_FMT, self.chunkdata_cmd_tlv_type, cmd_len) + cmd_info.encode('utf-8') 152 print(f"Packed command TLV{chunk_count}: {cmd_tlv}") 153 print(f'length:{cmd_len}') 154 # Write to binary file 155 package_file.seek(startoffset) 156 package_file.write(cmd_tlv) 157 startoffset += len(cmd_tlv) 158 159 # Step 3: Pack patch dependency data 160 data_tlv, patch_index, new_index = self.pack_dependency_data(image, cmd_info, chunk, patch_index, new_index) 161 # Write to bin file 162 data_len = len(data_tlv) 163 package_file.seek(startoffset) 164 package_file.write(data_tlv) 165 startoffset += data_len 166 167 except (struct.error) as e: 168 UPDATE_LOGGER.print_log(f"Unexpected error: {e}", log_type=UPDATE_LOGGER.ERROR_LOG) 169 UPDATE_LOGGER.print_log("write chunk error!", log_type=UPDATE_LOGGER.ERROR_LOG) 170 raise RuntimeError 171 except Exception as e: 172 UPDATE_LOGGER.print_log(f"An unexpected error occurred: {e}", log_type=UPDATE_LOGGER.ERROR_LOG) 173 UPDATE_LOGGER.print_log("write chunklist complete", log_type=UPDATE_LOGGER.ERROR_LOG) 174 raise RuntimeError 175 176 return startoffset 177 178 def write_hash_info(self, image_number, package_file, startoffset): 179 UPDATE_LOGGER.print_log("write image hash info StartOffset:%s"\ 180 % startoffset) 181 try: 182 # pack hash info head 183 hash_info_tlv = struct.pack(CHUNK_HASH_HEADER_FMT, 184 self.chunkhash_info_tlv_type, 185 2, image_number) 186 # Write to bin file 187 package_file.seek(startoffset) 188 package_file.write(hash_info_tlv) 189 startoffset += len(hash_info_tlv) 190 191 print(f"Packed image name TLV: {hash_info_tlv}") 192 193 except struct.error: 194 UPDATE_LOGGER.print_log("Pack fail!", log_type=UPDATE_LOGGER.ERROR_LOG) 195 raise RuntimeError 196 197 return startoffset 198 199 200 def write_image_hashdata(self, image_file, package_file, startoffset): 201 UPDATE_LOGGER.print_log("write image hash StartOffset:%s"\ 202 % startoffset) 203 try: 204 # Step 1: Pack hash partition name 205 image_name_tlv = struct.pack(CHUNK_HASH_PARTITION_FMT, 206 self.chunkhash_partition_tlv_type, 207 len(image_file)) + image_file.encode('utf-8') 208 partition_len = len(image_name_tlv) 209 # Write to bin file 210 package_file.seek(startoffset) 211 package_file.write(image_name_tlv) 212 startoffset += partition_len 213 214 print(f"Packed image name TLV: {image_name_tlv}") 215 216 # Step 2: Pack target partition hash value 217 each_image_file = os.path.join( 218 OPTIONS_MANAGER.target_package_dir, 219 '%s.img' % image_file) 220 image_hash_data = get_file_sha256(each_image_file) 221 image_hash_tlv = struct.pack(CHUNK_HASH_VALUE_FMT, 222 self.chunkhash_value_tlve_type, 223 len(image_hash_data)) + image_hash_data.lower().encode('utf-8') 224 image_hash_data_len = len(image_hash_tlv) 225 print(f'hash data:{image_hash_data.lower}') 226 # Write to bin file 227 package_file.seek(startoffset) 228 package_file.write(image_hash_tlv) 229 startoffset += image_hash_data_len 230 231 print(f"Packed hash data TLV: {image_hash_tlv}") 232 233 except struct.error: 234 UPDATE_LOGGER.print_log("Pack fail!", log_type=UPDATE_LOGGER.ERROR_LOG) 235 raise RuntimeError 236 return startoffset 237 238 def write_image_large(self, image_file, package_file, startoffset): 239 UPDATE_LOGGER.print_log("write image large StartOffset:%s"\ 240 % startoffset) 241 try: 242 if OPTIONS_MANAGER.full_img_list: 243 image_length = len(OPTIONS_MANAGER.full_image_new_data[image_file]) 244 else: 245 image_length = len(OPTIONS_MANAGER.diff_image_new_data[image_file]) 246 # Step 1: Pack hash partition name 247 image_large_tlv = struct.pack(CHUNK_LARGE_PARTITION_FMT, 248 self.chunkimage_large_tlv_type, 249 8) + image_length.to_bytes(8, byteorder='little') 250 partition_large_len = len(image_large_tlv) 251 # Write to bin file 252 package_file.seek(startoffset) 253 package_file.write(image_large_tlv) 254 startoffset += partition_large_len 255 256 print(f"Packed image large TLV: {image_large_tlv}") 257 258 except struct.error: 259 UPDATE_LOGGER.print_log("Pack fail!", log_type=UPDATE_LOGGER.ERROR_LOG) 260 raise RuntimeError 261 return startoffset 262 263 def write_all_image_signdata(self, signdata): 264 try: 265 signdata_len = len(signdata) 266 signdata_tlv = struct.pack(CHUNK_SIGN_VALUE_FMT, 267 self.chunksign_value_tlv_type, 268 signdata_len) 269 except struct.error: 270 UPDATE_LOGGER.print_log("Pack fail!", log_type=UPDATE_LOGGER.ERROR_LOG) 271 raise RuntimeError 272 273 # write signdata 274 self.signdata = signdata_tlv + signdata 275 UPDATE_LOGGER.print_log("Write hashdata sign tlv complete") 276 return True 277 278 def write_chunklist_full_image(self, image_name, package_file, chunks, block_sets, startoffset): 279 try: 280 UPDATE_LOGGER.print_log("write chunk StartOffset:%s"\ 281 % startoffset) 282 283 chunk_index = 0 284 for chunk in chunks: 285 # Step 1: Pack partition name 286 partition_tlv = struct.pack(CHUNK_DATA_PARTITION_FMT, 287 self.chunkdata_partition_tlv_type, 288 len(image_name)) + image_name.encode() 289 # Write to binary file 290 package_file.seek(startoffset) 291 package_file.write(partition_tlv) 292 startoffset += len(partition_tlv) 293 294 # Step 2: Pack command info 295 cmd_str = ("%s %s %d,%s,%s" % ("new", get_chunk_sha256(chunk), 2, 296 min(block_sets[chunk_index]), 297 max(block_sets[chunk_index]) + 1)) 298 cmd_tlv = struct.pack(CHUNK_DATA_CMD_FMT, 299 self.chunkdata_cmd_tlv_type, 300 len(cmd_str)) + cmd_str.encode() 301 # Write to binary file 302 package_file.seek(startoffset) 303 package_file.write(cmd_tlv) 304 startoffset += len(cmd_tlv) 305 306 # Step 3: Pack the sliced image data 307 data_tlv = struct.pack(CHUNK_DATA_DATA_FMT, 308 self.chunkdata_value_tlv_type, 309 len(chunk)) + chunk 310 # Write to binary file 311 package_file.seek(startoffset) 312 package_file.write(data_tlv) 313 startoffset += len(data_tlv) 314 chunk_index += 1 315 except struct.error: 316 UPDATE_LOGGER.print_log("Pack fail!", log_type=UPDATE_LOGGER.ERROR_LOG) 317 raise RuntimeError 318 319 return startoffset 320 321 def pack_dependency_data(self, image, cmd_info, chunk, patch_index, new_index): 322 if "pkgdiff" in cmd_info: 323 if OPTIONS_MANAGER.image_patch_dic[image]: 324 data_value = OPTIONS_MANAGER.image_patch_dic[image][patch_index] 325 if not data_value: 326 UPDATE_LOGGER.print_log("data_value is empty, using chunk instead.", 327 log_type=UPDATE_LOGGER.INFO_LOG) 328 data_value = chunk 329 data_tlv = struct.pack(CHUNK_DATA_DATA_FMT, 330 self.chunkdata_value_tlv_type, 331 len(data_value)) + data_value.encode('utf-8') 332 patch_index += 1 333 else: 334 data_tlv = struct.pack(CHUNK_DATA_DATA_FMT, 335 self.chunkdata_value_tlv_type, 336 len(data_value)) + data_value 337 patch_index += 1 338 else: 339 data_value = chunk 340 UPDATE_LOGGER.print_log("patch.data is empty!", log_type=UPDATE_LOGGER.ERROR_LOG) 341 raise RuntimeError 342 # Determine if the line in transfer.list contains new, if it does, take new.data with it. 343 elif "new" in cmd_info: 344 if OPTIONS_MANAGER.image_new_dic[image]: 345 data_value = OPTIONS_MANAGER.image_new_dic[image][new_index] 346 if not data_value: 347 UPDATE_LOGGER.print_log("data_value is empty, using chunk instead.", 348 log_type=UPDATE_LOGGER.INFO_LOG) 349 data_value = chunk 350 data_tlv = struct.pack(CHUNK_DATA_DATA_FMT, 351 self.chunkdata_value_tlv_type, 352 len(data_value)) + data_value.encode('utf-8') 353 new_index += 1 354 else: 355 data_tlv = struct.pack(CHUNK_DATA_DATA_FMT, 356 self.chunkdata_value_tlv_type, 357 len(data_value)) + data_value 358 new_index += 1 359 else: 360 data_value = chunk 361 UPDATE_LOGGER.print_log("new.data is empty!", log_type=UPDATE_LOGGER.ERROR_LOG) 362 raise RuntimeError 363 else: 364 # If none of the above instructions are met, put 0 in data_value 365 data_value = b'' 366 data_tlv = struct.pack(CHUNK_DATA_DATA_FMT, 367 self.chunkdata_value_tlv_type, 0) + data_value 368 return data_tlv, patch_index, new_index 369 370 371