• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# coding=utf-8
3#   Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
4#    Licensed under the Apache License, Version 2.0 (the "License");
5#    you may not use this file except in compliance with the License.
6#    You may obtain a copy of the License at
7#
8#        http://www.apache.org/licenses/LICENSE-2.0
9#
10#    Unless required by applicable law or agreed to in writing, software
11#    distributed under the License is distributed on an "AS IS" BASIS,
12#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13#    See the License for the specific language governing permissions and
14#    limitations under the License.
15
16"""
17* Description: NV binary create.
18* Create: 2021-11-30
19"""
20
21import os
22import re
23import sys
24import stat
25import json
26import hashlib
27import struct
28import shutil
29import zlib
30import binascii
31
32g_root = os.path.realpath(__file__)
33g_root = os.path.dirname(g_root)
34g_root = os.path.realpath(os.path.join(g_root, "..", "..", ".."))
35sys.path.append(os.path.join(g_root, 'build'))
36sys.path.append(os.path.join(g_root, 'build', 'script'))
37# print(g_root)
38
39from conf_parser import BuildConfParser, ParserError
40from build_utils import fn_str_to_int
41from generate_data_stream import generate_data_stream
42
43from ctypes import c_char, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, Structure, sizeof
44
45TD_CHAR = c_char
46TD_S8 = c_byte
47TD_U8 = c_ubyte
48TD_S16 = c_short
49TD_U16 = c_ushort
50TD_S32 = c_int
51TD_U32 = c_uint
52g_u8_max = 0xFF
53g_u16_max = 0xFFFF
54class KeyHead(Structure):
55    _fields_ = [
56        ("magic", TD_U8),    # Magic number to indicate the start of the item
57        ("valid", TD_U8),    # flag to indicate whether the value is valid
58        ("length", TD_U16),   # Length of the key_data field in bytes
59        ("type", TD_U8),      # Normal (0xFF) or permanent (0x00)  or keep (0x04  )
60        ("upgrade", TD_U8),
61
62        ("key_id", TD_U16),   # The Key ID
63        ("enc_key", TD_U16),   # Allows some customisation of the data AES key used, 0x0 - key_data is plaintext, Others - key_data is encrypted
64        ("version", TD_U16),  # Version of the key
65        ("rnd", TD_U32)     # Key header crc calculated from length
66    ]
67
68class NvPageHead(Structure):
69    _fields_ = [
70        ("id", TD_U16),
71        ("reserved", TD_U8),
72        ("num_pages", TD_U8),
73        ("inverted_details_word", TD_U32),
74        ("last_write", TD_U32), # last_write
75        ("unused", TD_U32), # We want this header to be 4 words long - this allows us to alias anything after it
76    ]
77
78# 1.根据指定的alias,合并所有配置文件
79# 1.1 解析alias中所有type=Nv的配置。
80
81# 2.如果需要,从合并的配置文件中提取NV项ID,生成ID枚举头文件提供给源码使用(构建过程不允许修改源码,因此该头文件只有需要的时候调用脚本生成)
82# 3.预编译所有nv结构
83# 4.解析配置文件中各个nv项的数据内容,与结构结合,生成bin
84
85
86
87class BuildNv:
88    def __init__(self, alias, root=None, targets=None, backup=False, use_crc16=False):
89        self.alias = alias
90        self.root = root if root is not None else g_root
91        self.targets = targets
92        self.is_backup = backup
93        self.use_crc16 = use_crc16
94        self.tmp_path = os.path.join(self.root, json_conf["BUILD_TEMP_PATH"])
95        self.nv_relative_path = os.path.join(self.root, json_conf["NV_RELATIVE_PATH"])
96        self.nv_root = os.path.join(self.root, json_conf["NV_DEFAULT_CFG_DIR"])
97        self.nv_output_dir = os.path.join(self.root, json_conf["OUT_BIN_DIR"])
98        if not backup:
99            self.nv_output_name = json_conf["OUT_BIN_NAME"]
100        else:
101            self.nv_output_name = json_conf["OUT_BACKUP_BIN_NAME"]
102        self.nv_ver_src_dict = dict()
103        self.nv_ver_dict = dict()
104        self.nv_flash_cfg = None
105        self.nv_cores_ver_bin = dict()
106        self.nv_chip_ver_bin = dict()
107        self.nv_flash_page_index = dict()
108
109    def set_nv_output_dir(self, path):
110        self.nv_output_dir = path
111
112    def start_work(self):
113        self._merge_cfgs()
114        self._load_nv_flash_cfg()
115        self._parse_etypes()
116        self._gen_binary()
117        self._create_header()
118
119
120    def _merge_cfgs(self):
121        '''
122        Merge config sources in self.nv_ver_src_dict.
123        This will build the self.nv_ver_dict like the following tree:
124                              |--- ver1 : {
125                              |            "merged_cfg" : json file after merge all nv configuration with the same product type.
126                              |            "prod_type" : "XXXX" }
127                              |
128            chip|---target1---|--- ver2 :
129                |             |
130                |             |--- ver3 :
131                |             |
132                |             |--- core : "Each target corresponds to one core."
133                |---targetN...
134        '''
135        for target in self.alias:
136            if self._nv_ver_prepare(target) is False:
137                continue
138        # print('nv_ver_src_dict: ', self.nv_ver_src_dict)
139
140        for chip in self.nv_ver_src_dict:
141            src_chip_dict = self.nv_ver_src_dict[chip]
142            # print("src_chip_dict =",src_chip_dict)
143            self.nv_ver_dict[chip] = {}
144            chip_dict = self.nv_ver_dict[chip]
145            for target in src_chip_dict:
146                if chip_dict.get(target) is None:
147                    chip_dict[target] = {'core':src_chip_dict[target]['core']}
148                nv_tmp_dir = os.path.join(self.nv_relative_path)
149                for ver_name in src_chip_dict[target]:
150                    if ver_name == 'core':
151                        continue
152                    cfg_file_prefix = os.path.join(nv_tmp_dir, 'cfg', '%s_nv' % (target)) # 生成中间文件路径
153                    # print("cfg_file_prefix = ", cfg_file_prefix)
154                    chip_dict[target][ver_name] = self._merge_ver_cfg(cfg_file_prefix, src_chip_dict[target][ver_name])
155
156    def _parse_etypes(self):
157        for chip in self.nv_ver_dict:
158            chip_dict = self.nv_ver_dict[chip]
159            for target in chip_dict:
160                # scons in chip dir or nv_config dir? etypes path depends on scons path
161                nv_tmp_dir = os.path.join(self.tmp_path, target)
162                etypes_path = os.path.join(nv_tmp_dir, "%s.etypes" % target) # 中间文件xxx.etypes路径
163                if os.path.exists(etypes_path) is not True: # 判断中间文件是否存在,如果不在说明该模块没有被编译,需要加入到编译链接中
164                    etypes_path = os.path.join(self.tmp_path, "etypes", "%s.etypes" % target)
165                    if os.path.exists(etypes_path) is not True:
166                        msg = "[error] [%s] need add nv_config module in alias! %s" % (target, etypes_path)
167                        raise ParserError(msg)
168                stream_gen = generate_data_stream()
169                stream_gen.phase_etypes(etypes_path)
170                chip_dict[target]["stream_gen"] = stream_gen
171        dtabase_txt = os.path.join(self.root, json_conf['DATABASE_TXT_FILE'])
172        shutil.copy(etypes_path, dtabase_txt)
173
174
175    def _gen_binary(self):
176        '''
177                            |--- ver1 : binary file of ver1.
178                            |   (version name :  product_type + version name)
179            chip|---core1---|--- ver2 :
180                |           |
181                |           |--- ver3 :
182                |---coreN...
183        '''
184        self._gen_binary_prepare()
185        self._gen_target_version_binary()
186        self._gen_chip_nv_binary()
187
188    def _gen_target_version_binary(self):
189        for chip in self.nv_cores_ver_bin:
190            cores = self.nv_cores_ver_bin[chip]
191            # print("cores =", cores)
192            for core in cores:
193                cores[core] = self._gen_version_binary(self.nv_ver_dict[chip], chip, core)
194
195    def _gen_version_binary(self, chip_ver_dict, chip, core):
196        ver_binary_dict = dict()
197        # print("chip_ver_dict = ", chip_ver_dict) # 字典信息包含核名字,配置文件路径
198        if chip_ver_dict is None:
199            return ver_binary_dict
200        for target in chip_ver_dict:
201            if chip_ver_dict[target].get('core') != core: # 判断字典里面的target读出来的core是否和core相同
202                print("chip_ver_dict[target].get('core') = ", chip_ver_dict[target].get('core'))
203                continue
204            stream_gen = chip_ver_dict[target].get('stream_gen')
205            for ver in chip_ver_dict[target]:
206                # print("ver =", ver)
207                if ver == 'core' or ver == 'stream_gen':
208                    continue
209                stream_gen = chip_ver_dict[target].get('stream_gen')
210                cfg_file = chip_ver_dict[target][ver]["merged_cfg"]
211                # print("cfg_file =",cfg_file)
212                stream = self._gen_nv_stream(cfg_file, stream_gen, chip, core)
213                nv_ver_bin = \
214                    os.path.join(self.nv_relative_path, 'bin', '%s_nv.bin' % (core))
215                # print("nv_ver_bin = ", nv_ver_bin) # 生成nvbin文件的路径
216                prod_type =  chip_ver_dict[target][ver]["prod_type"]
217                prod_type = "all" if prod_type is None else prod_type
218                ver_binary_dict["%s_%s" % (chip, prod_type)] = self._write_binary_to_file(nv_ver_bin, stream)
219        return ver_binary_dict
220
221    def _write_binary_to_file(self, file_path, stream):
222        if os.path.exists(os.path.dirname(file_path)) is False:
223            os.makedirs(os.path.dirname(file_path))
224        if os.path.exists(file_path) is True:
225            os.remove(file_path)
226        flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
227        modes = stat.S_IWUSR | stat.S_IRUSR
228        with os.fdopen(os.open(file_path, flags, modes), 'wb') as fout:
229            fout.write(stream)
230        return file_path
231
232    def _gen_binary_prepare(self):
233        for chip in self.nv_ver_dict:
234            self.nv_chip_ver_bin[chip] = dict()
235            self.nv_cores_ver_bin[chip] = dict()
236            self.nv_flash_page_index[chip] = dict()
237            nv_flash_chip_cfg = self.nv_flash_cfg[chip]
238            cores = nv_flash_chip_cfg["cores"].keys()
239            chip_nv_ver = self.nv_ver_dict[chip]
240            for target in chip_nv_ver:
241                core = chip_nv_ver[target].get('core')
242                for core in cores:
243                    if core not in cores:
244                        msg = "[error] [%s] [%s] not a core cfg in nv_storage_cfg.json!" % (target, core)
245                        raise ParserError(msg)
246                    if self.nv_cores_ver_bin[chip].get(core) is None:
247                        self.nv_cores_ver_bin[chip][core] = dict()
248            page_size = fn_str_to_int(nv_flash_chip_cfg["size"]["page_size"])
249            total_size = 0
250            for core in cores:
251                core_page_nums = nv_flash_chip_cfg['cores'][core]['page_nums']
252                total_size += core_page_nums * page_size
253                self.nv_flash_page_index[chip][core] = [(sizeof(NvPageHead) + num * page_size, (num + 1) * page_size) \
254                    for num in range(0, core_page_nums)]
255            if total_size > fn_str_to_int(nv_flash_chip_cfg["size"]['flash_size']):
256                msg = "[error] cores size: %s, over total flash size: %s!" % \
257                    (total_size, nv_flash_chip_cfg["size"]['flash_size'])
258                raise ParserError(msg)
259
260    def _gen_nv_stream(self, cfg_file, stream_gen, chip, core):
261        core_nv_bin = self._init_nv_page_head(chip, core)
262
263        cfg_data = BuildConfParser(cfg_file).get_conf_data()
264        key_id_list = []
265        last_key_item_start_addr = 0
266        for module in cfg_data:
267            for key_item in cfg_data[module]:
268                if key_item == 'module_id':
269                    continue
270                key_struct_name = cfg_data[module][key_item].get("structure_type")
271                key_value = cfg_data[module][key_item].get("value")
272                key_attr = cfg_data[module][key_item].get("attributions")
273                key_id = cfg_data[module][key_item].get("key_id")
274                key_id = fn_str_to_int(key_id) if type(key_id) is not int else key_id
275                if key_struct_name is None or key_value is None or key_attr is None or key_value == []:
276                    msg = "[error] 'structure_type' 'value' 'attributions' must be configured!"
277                    raise ParserError(msg)
278
279                if key_id in key_id_list:
280                    msg = "[error] key id:%d repeated, please check!" % key_id
281                    raise ParserError(msg)
282                key_id_list.append(key_id)
283
284                key_data, key_data_len = stream_gen.generate(key_struct_name, key_value)
285                page_index, key_item_start_addr = self._find_usable_addr(chip, core, key_data, key_data_len)
286                core_nv_bin, key_item_start_addr = \
287                    self._init_key_head(core_nv_bin, key_item_start_addr, key_data_len, key_id, key_attr)
288
289                core_nv_bin, key_item_start_addr = \
290                    self._set_key_data(core_nv_bin, key_item_start_addr, key_data, key_data_len)
291                core_nv_bin, key_item_start_addr = \
292                    self._set_key_hash(core_nv_bin, key_item_start_addr, key_data_len)
293                self._update_core_index(chip, core, page_index, key_item_start_addr)
294                last_key_item_start_addr = max(last_key_item_start_addr, key_item_start_addr)
295        core_nv_bin = self._set_unused_page(chip, core_nv_bin, last_key_item_start_addr)
296        self._reset_core_index(chip, core)
297        return core_nv_bin
298
299    def _set_unused_page(self, chip, core_nv_bin, key_item_start_addr):
300        page_size = fn_str_to_int(self.nv_flash_cfg[chip]['size']['page_size'])
301        core_nv_end_addr = (key_item_start_addr + (page_size - 1)) & ~(page_size - 1)
302        for i in range(key_item_start_addr, core_nv_end_addr):
303            core_nv_bin[i] = 0xFF
304        return core_nv_bin[0 : core_nv_end_addr]
305
306    def _gen_chip_nv_binary(self):
307        for chip in self.nv_cores_ver_bin:
308            chip_bins = self.nv_cores_ver_bin[chip]
309            ver_list = []
310            for core in chip_bins:
311                ver_list.extend(chip_bins[core].keys())
312
313            ver_list = set(ver_list)
314            for ver in ver_list:
315                self.nv_chip_ver_bin[chip][ver] = self._assemble_ver_bins(chip, ver)
316
317    def _assemble_ver_bins(self, chip, ver):
318        flash_bin = bytearray(fn_str_to_int(self.nv_flash_cfg[chip]['size']['flash_size']))
319        for i in range(0, len(flash_bin)):
320            flash_bin[i] = 0xFF
321
322        start_addr = 0
323        chip_bins = self.nv_cores_ver_bin[chip]
324        cores = self.nv_cores_ver_bin[chip]
325        for core in chip_bins:
326            ver_bin = chip_bins[core].get(ver)
327            flash_bin, start_addr = self._append_file_to_stream(flash_bin, start_addr, chip, core, ver_bin)
328            chip_ver_bin_file = os.path.join(self.nv_output_dir, self.nv_output_name)
329        # 目前只考虑单核场景去除 NV bin 末尾的无用 FF
330        return self._write_binary_to_file(chip_ver_bin_file, flash_bin[0 : start_addr])
331
332    def _append_file_to_stream(self, flash_bin, start_addr, chip, core, ver_bin):
333        core_bin_size = fn_str_to_int(self.nv_flash_cfg[chip]['size']["page_size"]) * \
334                        self.nv_flash_cfg[chip]["cores"][core]['page_nums']
335        core_nv_bin = b''
336        if ver_bin is None:
337            core_nv_bin = self._init_nv_page_head(chip, core)
338        else:
339            with open(ver_bin, 'rb') as f:
340                core_nv_bin = f.read()
341        #print('core_nv_bin = ', core_nv_bin)
342        tail_addr = start_addr + len(core_nv_bin)
343        flash_bin[start_addr : tail_addr] = core_nv_bin
344
345        return flash_bin, tail_addr
346
347    def _reset_core_index(self, chip, core):
348        nv_flash_chip_cfg = self.nv_flash_cfg[chip]
349        page_size = fn_str_to_int(nv_flash_chip_cfg['size']["page_size"])
350        core_page_nums = nv_flash_chip_cfg['cores'][core]['page_nums']
351        self.nv_flash_page_index[chip][core] = [(sizeof(NvPageHead) + num * page_size, (num + 1) * page_size) \
352            for num in range(0, core_page_nums)]
353
354    def _update_core_index(self, chip, core, index, addr):
355        (start_addr, page_max_addr) = self.nv_flash_page_index[chip][core][index]
356        if start_addr >= addr or addr > page_max_addr:
357            msg = "[error] addr %s invalid!" % addr
358            raise ParserError(msg)
359        self.nv_flash_page_index[chip][core][index] = (addr, page_max_addr)
360        #print("update page index: \n", self.nv_flash_page_index)
361
362    def _find_usable_addr(self, chip, core, key_data, key_data_len):
363        page_size = fn_str_to_int(self.nv_flash_cfg[chip]['size'].get('page_size'))
364        key_item_total_len = self._get_key_item_len(key_data_len)
365        if key_item_total_len > page_size - sizeof(NvPageHead):
366            msg = "[error] key over page size !" % key_id
367            raise ParserError(msg)
368
369        index = 0
370        for (start_addr, page_max_addr) in self.nv_flash_page_index[chip][core]:
371            if start_addr + key_item_total_len > page_max_addr:
372                index += 1
373                continue
374            return index, start_addr
375        msg = "[error] no more enough space for [%s]!" % core
376        raise ParserError(msg)
377
378    def _get_key_type_from_attr(self, key_id, attr):
379        if (attr & 1) and (not (attr & ~1)):
380            return 0xFF
381        elif (attr & 2) and (not (attr & ~2)):
382            return 0x00
383        elif (attr & 4) and (not (attr & ~4)):
384            return 0xFF
385        else:
386            msg = "[error] attribution config err: [id-%s] [attr-%s] !" % (key_id, attr)
387            raise ParserError(msg)
388
389    def _get_key_upgrade_from_attr(self, key_id, attr):
390        if (attr & 1) and (not (attr & ~1)):
391            return 0xFF
392        elif (attr & 2) and (not (attr & ~2)):
393            return 0xFF
394        elif (attr & 4) and (not (attr & ~4)):
395            return 0x00
396        else:
397            msg = "[error] attribution config err: [id-%s] [attr-%s] !" % (key_id, attr)
398            raise ParserError(msg)
399
400    def _get_key_item_len(self, key_data_len):
401        if key_data_len % 4 != 0:
402            key_data_len += 4 - key_data_len % 4
403        return sizeof(KeyHead) + 4 + key_data_len
404
405    def _set_key_hash(self, nv_bin, key_item_start_addr, key_data_len):
406        if key_data_len % 4 != 0:
407            key_data_len += 4 - key_data_len % 4
408        hash_start_addr = key_item_start_addr - key_data_len - sizeof(KeyHead)
409        hash_end_addr = hash_start_addr + key_data_len + sizeof(KeyHead)
410        if not self.use_crc16:
411            crc32num = zlib.crc32(nv_bin[hash_start_addr : hash_end_addr])
412        else:
413            crc32num = binascii.crc_hqx(nv_bin[hash_start_addr : hash_end_addr], 0)
414        crc32ret = '{:0>8X}'.format(crc32num)
415        crc32ret = re.sub(r"(?<=\w)(?=(?:\w\w)+$)", " 0x", crc32ret)
416        crc32ret = '0x' + crc32ret
417        crc32list = [int(x,16) for x in crc32ret.split(" ")]
418        sha256bytearray = bytes(crc32list)
419        tail_addr = key_item_start_addr + len(sha256bytearray)
420        nv_bin[key_item_start_addr : tail_addr] = sha256bytearray
421        return nv_bin, tail_addr
422
423    def _set_key_data(self, nv_bin, key_item_start_addr, key_data, key_data_len):
424        if key_data_len % 4 != 0:
425            for i in range(0, 4 - key_data_len % 4) :
426                key_data += b'\x00'
427            key_data_len += 4 - key_data_len % 4
428        tail_addr = key_item_start_addr + key_data_len
429        nv_bin[key_item_start_addr : tail_addr] = key_data
430        return nv_bin, tail_addr
431
432    def _init_key_head(self, nv_bin, key_item_start_addr, key_data_len, key_id, key_attr):
433        nv_key_st = KeyHead.from_buffer(nv_bin[key_item_start_addr:])
434
435        nv_key_st.magic = 0xA9
436        nv_key_st.length = key_data_len
437        nv_key_st.type = self._get_key_type_from_attr(key_id, key_attr)
438        nv_key_st.upgrade = self._get_key_upgrade_from_attr(key_id, key_attr)
439        nv_key_st.key_id = key_id
440
441        nv_key_st.version = 65535
442        nv_key_st.enc_key = 0 # 目前不支持加密,且nv加密部分不由nv脚本做,可能放到初始化?
443        tail_addr = key_item_start_addr + sizeof(KeyHead)
444        nv_bin[key_item_start_addr : tail_addr] = nv_key_st
445        return nv_bin, tail_addr
446
447    def _init_nv_page_head(self, chip, core):
448        nv_flash_chip_cfg = self.nv_flash_cfg[chip]
449        default_page_nums = nv_flash_chip_cfg.get('default_page_nums')
450        page_size = fn_str_to_int(nv_flash_chip_cfg['size']['page_size'])
451        page_nums = nv_flash_chip_cfg['cores'][core]['page_nums']
452        if not self.is_backup:
453            page_id_start = nv_flash_chip_cfg['cores'][core]['page_id_start']
454        else:
455            page_id_start = '0x34B2'
456        core_nv_size = page_nums * page_size
457        core_nv_bin = bytearray(core_nv_size)
458
459        for i in range(0, core_nv_size):
460            core_nv_bin[i] = 0xFF
461
462        for i in range(0, page_nums):
463            start_addr = i * page_size
464            nv_page_head = NvPageHead.from_buffer(core_nv_bin[start_addr:])
465            nv_page_head.id = fn_str_to_int(page_id_start)
466            nv_page_head.reserved = 1
467            nv_page_head.num_pages = i
468            nv_page_head.inverted_details_word = ~int.from_bytes(struct.pack('HBB', \
469                nv_page_head.id, nv_page_head.reserved, nv_page_head.num_pages), 'little')
470            nv_page_head.last_write = 0
471            nv_page_head.unused = ~nv_page_head.last_write
472            core_nv_bin[start_addr : start_addr + sizeof(NvPageHead)] = nv_page_head
473        return core_nv_bin
474
475    def _load_nv_flash_cfg(self):
476        self.nv_flash_cfg = dict()
477        for chip in self.nv_ver_dict:
478            cfg_file = os.path.join(self.root, json_conf["NV_TARGET_JSON_PATH"])
479            self.nv_flash_cfg[chip] = BuildConfParser(cfg_file).get_conf_data()
480
481    def _add_nv_ver(self, chip, target, core, ver, common_cfg, ver_cfg, prod_type=None):
482        '''
483        Add version config into self.nv_ver_src_dict.
484        There are three configuration scenarios.One target may correspond to multiple NV versions.
485
486                              |--- ver1: { srcs:[default, common, cfg1], prod_type: }
487                              |
488            chip|---target1---|--- ver2: { srcs:[default, cfg2],         prod_type: }
489                |             |
490                |             |--- ver3: { srcs:[default, common],       prod_type: }
491                |             |
492                |             |--- core : "Each target corresponds to one core."
493                |---targetN...
494        '''
495
496        ver_cfgs = []
497        if os.path.exists(common_cfg) is True:
498            ver_cfgs.append(common_cfg)
499        if ver_cfg is not None and os.path.exists(ver_cfg):
500            ver_cfgs.append(ver_cfg)
501
502        if self.nv_ver_src_dict.get(chip) is None:
503            self.nv_ver_src_dict[chip] = dict()
504
505        chip_dict = self.nv_ver_src_dict[chip]
506        if chip_dict.get(target) is not None and chip_dict[target].get(ver) is not None:
507            msg = "[error] Ver config Repeate!"
508            raise ParserError(msg)
509
510        if chip_dict.get(target) is None:
511            chip_dict[target] = {ver:{"srcs":ver_cfgs, 'prod_type': prod_type}}
512        else:
513            chip_dict[target].update({ver:{"srcs":ver_cfgs, 'prod_type': prod_type}})
514
515        if chip_dict[target].get('core') is None:
516            chip_dict[target]['core'] = core
517        elif chip_dict[target].get('core') != core:
518            msg = "[error] [%s] core not match!" % target
519            raise ParserError(msg)
520
521    def _nv_ver_prepare(self, target):
522        '''
523            1. Check nv configurations.
524            2. Add all correct config into self.nv_ver_src_dict..
525        '''
526        if type(self.alias[target]) is list:
527            return False
528        target_type = self.alias[target].get("TYPE")
529        if target_type is None or target_type != 'nv':
530            return False
531
532        core = self.alias[target].get("CORE")
533        if core is None:
534            msg = "[error] core name not exist!"
535            raise ParserError(msg)
536        chip = self.alias[target].get("CHIP")
537        #default_cfg = os.path.join(self.nv_root, '%s_default.json' % core)
538        '''
539        if chip is None or os.path.exists(default_cfg) is False:
540            msg = "[error] chip name OR %s not exist!" % default_cfg
541            raise ParserError(msg)
542        '''
543        kernel_name = self.alias[target].get("KERNEL_BIN")
544        if kernel_name is None:
545            msg = "[error] KERNEL is null!"
546            raise ParserError(msg)
547        if self.targets is not None and kernel_name not in self.targets:
548            return False
549        cfgs = self.alias[target].get("COMPONENT")
550        if cfgs is None:
551            msg = "[error] COMPONENT is null!"
552            raise ParserError(msg)
553
554        prod_type = self.alias[target].get("PRODUCT_TYPE")
555        if prod_type == "":
556            prod_type = None
557        if prod_type is not None and type(prod_type) is not str:
558            msg = "[error] PRODUCT_TYPE must be a string type, one kernel only suuport one product type!"
559            raise ParserError(msg)
560
561        cfg_dir = os.path.join(self.nv_root, kernel_name)
562        common_cfg = os.path.join(cfg_dir, 'common.json')
563
564        for cfg in cfgs:
565            cfg_file = os.path.join(cfg_dir, '%s.json' % cfg) if cfg != 'common' else None
566            self._add_nv_ver(chip, kernel_name, core, cfg, common_cfg, cfg_file, prod_type)
567
568    def _prod_type_filter(self, srcs, prod_type = None):
569        combination = dict()
570        module_dict = dict()
571        for src in srcs:
572            # print("[INFO] nv config src file: ", src)
573            src_conf = BuildConfParser(src).get_conf_data()
574            for module in src_conf:
575                module_id = src_conf.get(module).get('module_id')
576                if module_id is None:
577                    msg = "[error][file:%s][%s] module_id is null!" % (src, module)
578                    raise ParserError(msg)
579                module_id = fn_str_to_int(module_id) if type(module_id) is not int else module_id
580                if module_id > g_u8_max :
581                    msg = "[error][file:%s][%s] module_id is more than 0xFF!" % (src, module)
582                    raise ParserError(msg)
583
584                # diffrent module must config with diffrent module_id
585                if module_id in module_dict:
586                    if module_dict[module_id] != module:
587                        msg = "[error][file:%s][%s] module_id is the same to [%s]!" % (src, module, module_dict[module_id])
588                        raise ParserError(msg)
589                else:
590                    module_dict[module_id] = module
591
592                if module not in combination:
593                    combination[module] = {'module_id': module_id}
594                elif combination.get(module).get('module_id') != module_id:
595                    msg = "[error][%s][%s] module_id is not same as other file!" % (src, module)
596                    raise ParserError(msg)
597
598                for item in src_conf.get(module):
599                    key_cfg = src_conf.get(module).get(item)
600                    if item == 'module_id':
601                        continue
602
603                    key_id = key_cfg.get('key_id')
604                    key_id = None if key_id is None else (fn_str_to_int(key_id) if type(key_id) is not int else key_id)
605                    # print("key id: %d, module id: %d, key>>8: %d"%( key_id, module_id, key_id>>8))
606                    if key_id is None or key_id > g_u16_max :
607                        msg = "[error][file:%s][%s][%s] key_id is null or more than unsighed 16 or not match with module_id!" % (src, module, item)
608                        raise ParserError(msg)
609
610                    item_prod_type = key_cfg.get('product_type')
611                    key_status = key_cfg.get('key_status')
612                    #print('prodtype: %s, key prod: %s'%(prod_type, item_prod_type))
613                    if (prod_type == item_prod_type or (item_prod_type is not None and prod_type in item_prod_type)) \
614                        and key_status == 'alive':
615                        combination[module].update({item:key_cfg})
616        return combination
617
618    def _nv_cfg_writer(self, dst_file, combination):
619        if os.path.exists(os.path.dirname(dst_file)) is False:
620            os.makedirs(os.path.dirname(dst_file))
621        if os.path.exists(dst_file):
622            os.remove(dst_file)
623        flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
624        modes = stat.S_IWUSR | stat.S_IRUSR
625        with os.fdopen(os.open(dst_file, flags, modes), 'w') as fout:
626            fout.write(json.dumps(combination, indent=4))
627
628    def _merge_ver_cfg(self, file_prefix, ver_cfg):
629        srcs = ver_cfg.get('srcs')
630        if srcs is None:
631            msg = "[error] ver cfg file is null!"
632            raise ParserError(msg)
633
634        prod_type = ver_cfg.get('prod_type')
635        combination = self._prod_type_filter(srcs, prod_type)
636        dst_file = '%s.json' % file_prefix
637        self._nv_cfg_writer(dst_file, combination)
638        return { "merged_cfg" : dst_file, "prod_type" : prod_type}
639
640    def _create_header(self):
641        pass
642
643# 检查配置文件中是否有必须配置项
644def check_key(json_conf):
645    check_item = ['OUT_BIN_DIR', 'BUILD_TEMP_PATH', 'NV_TARGET_JSON_PATH', \
646        'NV_RELATIVE_PATH', 'NV_DEFAULT_CFG_DIR', 'DATABASE_TXT_FILE']
647    keys = dict.keys(json_conf)
648    for check_key in check_item:
649        if check_key not in keys:
650            msg = "[error] [nv_binary] need add ConfigMap (%s) in json_conf!" % (check_key)
651            raise ParserError(msg)
652
653def test(targets, flag, backup, use_crc16):
654    root = g_root
655    nv_target_json_path = os.path.join(root, json_conf["NV_TARGET_JSON_PATH"])
656    alias_conf = BuildConfParser(nv_target_json_path).get_conf_data()
657    worker = BuildNv(alias_conf, root, targets, backup, use_crc16)
658    if flag:
659        worker.set_nv_output_dir(os.path.join(root, json_conf["OUT_BIN_DIR"]))
660    worker.start_work()
661
662def nv_begin(in_path, targets, flag, gen_backup=False, use_crc16=False):
663    global json_conf
664    with open(in_path, 'r') as i:
665        json_conf = json.load(i)
666
667    check_key(json_conf)
668    test(targets, flag, False, use_crc16)
669    if gen_backup:
670        test(targets, flag, True, use_crc16)
671    print("build nv bin success!!")
672
673if __name__ == "__main__":
674    in_path = sys.argv[2]
675    targets = sys.argv[3].split()
676    flag = len(sys.argv) == 3
677
678    nv_begin(in_path, targets, flag)
679