• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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