Infineon: Switch to 1.9.0 code base, add xmc7000 family support, refactor memory layer
diff --git a/boot/cypress/scripts/feature.py b/boot/cypress/scripts/feature.py
new file mode 100644
index 0000000..4bc428c
--- /dev/null
+++ b/boot/cypress/scripts/feature.py
@@ -0,0 +1,251 @@
+"""
+Copyright 2023 Cypress Semiconductor Corporation (an Infineon company)
+or an affiliate of Cypress Semiconductor Corporation. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+
+import sys
+import json
+import click
+
+def load_json(file_path):
+ """
+ Loads JSON from file.
+ """
+
+ data_json = None
+
+ try:
+ with open(file_path, encoding="utf-8") as file:
+ data_json = json.load(file)
+
+ except FileNotFoundError:
+ print(f'\nERROR: Cannot find {file_path}')
+ sys.exit(-1)
+
+ return data_json
+
+
+class FieldsValidator:
+ """
+ Validation of required fields and their cross-dependencies.
+ """
+
+ @staticmethod
+ def validate(feature_json, properties_json):
+ """
+ Check 'target' and properties of a platform.
+ """
+ p_target = properties_json.get('target')
+ if p_target is None:
+ raise AttributeError('Field "target" must be present in platform_properties.json')
+
+ f_target = feature_json.get('target')
+ if f_target is None:
+ raise AttributeError('Field "target" must be present in a feature_config.json')
+
+ if f_target not in p_target:
+ raise AttributeError('Target in feature config is not correct.'
+ ' It must be among the target list of platform_properties.json')
+
+ f_security_setup = feature_json.get('security_setup')
+ p_security_setup = properties_json.get('security_setup')
+
+ if f_security_setup:
+
+ if p_security_setup is None:
+ raise AttributeError("This platform doesn't have any 'secure_setup' features")
+
+ if f_security_setup.get('hw_rollback_prot'):
+ if p_security_setup.get('hw_rollback_prot') is None:
+ raise AttributeError("This platform doesn't have HW anti roll-back counter")
+
+ if f_security_setup.get('hw_crypto_acceleration'):
+ if p_security_setup.get('hw_crypto_acceleration') is None:
+ raise AttributeError("The platform doesn't support HW crypto acceleration")
+
+ if f_security_setup.get('validate_upgrade').get('value') is False:
+ raise AttributeError("Deactivation of image validation during the upgrade \
+ process isn't implemented yet")
+
+
+class FeatureProcessor:
+
+ """
+ The general handler of all needed fields and filling the new mk-file.
+ """
+
+ settings_dict = {
+ 'validate_boot' : 'MCUBOOT_SKIP_IMAGE_VALIDATION',
+ 'validate_upgrade' : 'MCUBOOT_SKIP_UPGRADE_VALIDATION',
+ 'dependency_check' : 'MCUBOOT_DEPENDENCY_CHECK',
+ 'serial_logging' : 'MCUBOOT_LOG_LEVEL',
+ 'hw_rollback_prot' : 'USE_HW_ROLLBACK_PROT',
+ 'hw_crypto_acceleration' : "USE_CRYPTO_HW",
+ 'sw_downgrade_prev' : 'USE_SW_DOWNGRADE_PREV',
+ 'ram_app_staging' : 'USE_STAGE_RAM_APPS',
+ 'xip' : 'USE_XIP',
+ 'image_encryption' : 'ENC_IMG',
+ 'fault_injection_hardening' : 'FIH_PROFILE_LEVEL',
+ 'combine_hex' : 'COMBINE_HEX',
+ 'hw_key' : 'USE_HW_KEY'
+ }
+
+ debug_level_dict = {
+ 'off' : '_OFF',
+ 'error' : '_ERROR',
+ 'warning' : '_WARNING',
+ 'info' : '_INFO',
+ 'debug' : '_DEBUG'
+ }
+
+ fih_level_dict = {
+ 'off' : 'OFF',
+ 'low' : 'LOW',
+ 'medium' : 'MEDIUM',
+ 'high' : 'HIGH'
+ }
+
+ def __init__(self, output_name):
+ self.out_f = output_name
+
+ @staticmethod
+ def generate_header_guard():
+ """
+ Print header line at the begining of a mk-file
+ """
+ guard_lines = ('# AUTO-GENERATED FILE, DO NOT EDIT.'
+ ' ALL CHANGES WILL BE LOST! #\n\n')
+
+ return guard_lines
+
+ @staticmethod
+ def insert_res(val_to_check) -> str:
+ """
+ Simlpe check result and return the string with value.
+ """
+ return f' := {1 if val_to_check else 0}\n'
+
+ @staticmethod
+ def insert_inverted_res(val_to_check) -> str:
+ """
+ Simlpe check result and return the string with inverted value.
+ """
+ return f' := {0 if val_to_check else 1}\n'
+
+ def __prnt_dict_primitive_key(self, dict_feature_config, settings_dict_key, f_out):
+ """
+ Print kyes of 'feature_config' with bool type of 'value'
+ """
+ val = dict_feature_config.get(settings_dict_key).get('value')
+
+ if isinstance(val, bool):
+
+ # invert because variable use 'skip' command
+ need_invertion = set(("validate_boot", "validate_upgrade"))
+
+ f_out.write(self.settings_dict[settings_dict_key])
+
+ if settings_dict_key not in need_invertion:
+ f_out.write(FeatureProcessor.insert_res(val))
+ else:
+ f_out.write(FeatureProcessor.insert_inverted_res(val))
+
+
+ def __gen_fih_level(self, fih_value):
+ """
+ Print only FIH_
+ """
+ res = f"{self.settings_dict['fault_injection_hardening']} ?= "\
+ f"{self.fih_level_dict[fih_value]}\n"
+
+ return res
+
+ def __gen_debug_level(self, logging_value):
+ """
+ Print only MCUBOOT_LOG_LEVEL
+ """
+ param_txt = self.settings_dict['serial_logging']
+ res_str = f"{param_txt} ?= {param_txt}{self.debug_level_dict[logging_value]}\n"
+
+ return res_str
+
+
+ def __handle_dictionary(self, f_dict, f_out):
+ """
+ Handle any dictionary of 'feature_config'
+ """
+ dont_print_list = set(("validation_key", "version", "description", "target"))
+
+ for k in f_dict:
+
+ if k not in dont_print_list:
+ self.__prnt_dict_primitive_key(f_dict, k, f_out)
+
+ if k == 'fault_injection_hardening':
+ f_out.write(self.__gen_fih_level(f_dict.get(k).get("value")))
+
+ if k == 'serial_logging':
+ f_out.write(self.__gen_debug_level(f_dict.get(k).get("value")))
+
+
+ def make_file_generate(self, feature_json):
+ """
+ Processing all keys and creation of a mk-file
+ """
+
+ with open(self.out_f, "w", encoding='UTF-8') as f_out:
+ f_out.write(FeatureProcessor.generate_header_guard())
+
+ f_security_setup_dict = feature_json.get('security_setup')
+
+ # handling of 'security_setup' section
+ if f_security_setup_dict:
+ self.__handle_dictionary(f_security_setup_dict, f_out)
+
+ self.__handle_dictionary(feature_json, f_out)
+
+
+@click.group()
+def cli():
+ """
+ Feature config parser to run from CLI
+ """
+
+@cli.command()
+@click.option('-f', '--feature_config', required=True,
+ help='feature configuration file path')
+@click.option('-p', '--platform_properties', required=True,
+ help='platform properties file path')
+@click.option('-n', '--output_name', required=True,
+ help='the name of the make file that will be generated')
+
+
+def run(feature_config, platform_properties, output_name):
+ """
+ The main CLI command to run mk-file generation
+ """
+
+ feature_config_json = load_json(feature_config)
+ platform_properties_json = load_json(platform_properties)
+
+ FieldsValidator.validate(feature_config_json, platform_properties_json)
+
+ fprocessor = FeatureProcessor(output_name)
+ fprocessor.make_file_generate(feature_config_json)
+
+
+if __name__ == '__main__':
+ cli()
diff --git a/boot/cypress/scripts/find_cysectools.py b/boot/cypress/scripts/find_cysectools.py
index 9aae88e..7bea0fc 100644
--- a/boot/cypress/scripts/find_cysectools.py
+++ b/boot/cypress/scripts/find_cysectools.py
@@ -17,7 +17,7 @@
import subprocess
import sys
-package = 'cysecuretools'
+package = 'cysecuretools'
def find_cysectools(package_name):
diff --git a/boot/cypress/scripts/flashmap.py b/boot/cypress/scripts/flashmap.py
deleted file mode 100644
index 856e430..0000000
--- a/boot/cypress/scripts/flashmap.py
+++ /dev/null
@@ -1,935 +0,0 @@
-"""MCUBoot Flash Map Converter (JSON to .h)
-Copyright (c) 2022 Infineon Technologies AG
-"""
-
-import sys
-import getopt
-import json
-
-# Supported Platforms
-cm0pCore = {
- 'cortex-m0+': 'CM0P',
- 'cm0+': 'CM0P',
- 'm0+': 'CM0P',
- 'cortex-m0p': 'CM0P',
- 'cm0p': 'CM0P',
- 'm0p': 'CM0P',
- 'cortex-m0plus': 'CM0P',
- 'cm0plus': 'CM0P',
- 'm0plus': 'CM0P'
-}
-
-cm4Core = {
- 'cortex-m4': 'CM4',
- 'cm4': 'CM4',
- 'm4': 'CM4'
-}
-
-cm33Core = {
- 'cortex-m33': 'CM33',
- 'cm33': 'CM33',
- 'm33': 'CM33'
-}
-
-allCores_PSOC_06x = {**cm0pCore, **cm4Core}
-
-common_PSOC_061 = {
- 'flashAddr': 0x10000000,
- 'eraseSize': 0x200, # 512 bytes
- 'smifAddr': 0x18000000,
- 'smifSize': 0x8000000, # i.e., window size
- 'VTAlign': 0x400, # Vector Table alignment
- 'allCores': cm4Core,
- 'bootCore': 'Cortex-M4',
- 'appCore': 'Cortex-M4'
-}
-
-common_PSOC_06x = {
- 'flashAddr': 0x10000000,
- 'eraseSize': 0x200, # 512 bytes
- 'smifAddr': 0x18000000,
- 'smifSize': 0x8000000, # i.e., window size
- 'VTAlign': 0x400, # Vector Table alignment
- 'allCores': allCores_PSOC_06x,
- 'bootCore': 'Cortex-M0+',
- 'appCore': 'Cortex-M4'
-}
-
-platDict = {
- 'PSOC_061_2M': {
- 'flashSize': 0x200000, # 2 MBytes
- **common_PSOC_061
- },
- 'PSOC_061_1M': {
- 'flashSize': 0x100000, # 1 MByte
- **common_PSOC_061
- },
- 'PSOC_061_512K': {
- 'flashSize': 0x80000, # 512 KBytes
- **common_PSOC_061
- },
-
- 'PSOC_062_2M': {
- 'flashSize': 0x200000, # 2 MBytes
- **common_PSOC_06x
- },
- 'PSOC_062_1M': {
- 'flashSize': 0x100000, # 1 MByte
- **common_PSOC_06x
- },
- 'PSOC_062_512K': {
- 'flashSize': 0x80000, # 512 KBytes
- **common_PSOC_06x
- },
-
- 'PSOC_063_1M': {
- 'flashSize': 0x100000, # 1 MByte
- **common_PSOC_06x
- },
-
- 'CYW20829': {
- 'flashSize': 0, # n/a
- 'smifAddr': 0x60000000,
- 'smifSize': 0x8000000, # i.e., window size
- 'VTAlign': 0x200, # Vector Table alignment
- 'allCores': cm33Core,
- 'bootCore': 'Cortex-M33',
- 'appCore': 'Cortex-M33',
- 'bitsPerCnt': False
- },
-
-
-}
-
-# Supported SPI Flash ICs
-flashDict = {
- # Fudan
- 'FM25Q04': {
- 'flashSize': 0x80000, # 4 Mbits
- 'eraseSize': 0x1000, # 128 uniform sectors with 4K-byte each
- },
- 'FM25W04': {
- 'flashSize': 0x80000, # 4 Mbits
- 'eraseSize': 0x1000, # 128 uniform sectors with 4K-byte each
- },
- 'FM25Q08': {
- 'flashSize': 0x100000, # 8 Mbits
- 'eraseSize': 0x1000, # 256 uniform sectors with 4K-byte each
- },
- 'FM25W08': {
- 'flashSize': 0x100000, # 8 Mbits
- 'eraseSize': 0x1000, # 256 uniform sectors with 4K-byte each
- },
- # Puya
- 'P25Q05H': {
- 'flashSize': 0x10000, # 512 Kbits
- 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
- },
- 'P25Q10H': {
- 'flashSize': 0x20000, # 1 Mbit
- 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
- },
- 'P25Q20H': {
- 'flashSize': 0x40000, # 2 Mbits
- 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
- },
- 'P25Q40H': {
- 'flashSize': 0x80000, # 4 Mbits
- 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
- },
- # Infineon
- 'S25HS256T': {
- 'flashSize': 0x2000000, # 256 Mbits
- 'eraseSize': 0x40000, # Uniform Sector Architecture
- },
- 'S25HS512T': {
- 'flashSize': 0x4000000, # 512 Mbits
- 'eraseSize': 0x40000, # Uniform Sector Architecture
- },
- 'S25HS01GT': {
- 'flashSize': 0x8000000, # 1 Gbit
- 'eraseSize': 0x40000, # Uniform Sector Architecture
- }
-}
-
-
-def is_overlap(fa1off, fa1size, fa2off, fa2size, align):
- """Check if two flash areas on the same device overlap"""
- mask = align - 1
- assert align > 0 and (align & mask) == 0 # ensure align is a power of 2
- fa1end = (fa1off + fa1size + mask) & ~mask
- fa2end = (fa2off + fa2size + mask) & ~mask
- fa1off = fa1off & ~mask
- fa2off = fa2off & ~mask
- return fa1off < fa2end and fa2off < fa1end
-
-
-def is_same_mem(fa1addr, fa2addr):
- """Check if two addresses belong to the same memory"""
- if fa1addr is None or fa2addr is None:
- return False
- mask = 0xFF000000
- return (fa1addr & mask) == (fa2addr & mask)
-
-
-class CmdLineParams:
- """Command line parameters"""
-
- def __init__(self):
- self.plat_id = ''
- self.in_file = ''
- self.out_file = ''
- self.img_id = None
- self.policy = None
- self.set_core = False
-
- usage = 'USAGE:\n' + sys.argv[0] + \
- ''' -p <platform> -i <flash_map.json> -o <flash_map.h> -d <img_id>
-
-OPTIONS:
--h --help Display the usage information
--p --platform= Target (e.g., PSOC_062_512K)
--i --ifile= JSON flash map file
--o --ofile= C header file to be generated
--d --img_id ID of application to build
--c --policy Policy file in JSON format
--m --core Detect and set Cortex-M CORE
-'''
-
- try:
- opts, unused = getopt.getopt(
- sys.argv[1:], 'hi:o:p:d:c:m',
- ['help', 'platform=', 'ifile=', 'ofile=', 'img_id=', 'policy=', 'core'])
- if len(unused) > 0:
- print(usage, file=sys.stderr)
- sys.exit(1)
- except getopt.GetoptError:
- print(usage, file=sys.stderr)
- sys.exit(1)
-
- for opt, arg in opts:
- if opt in ('-h', '--help'):
- print(usage, file=sys.stderr)
- sys.exit()
- elif opt in ('-p', '--platform'):
- self.plat_id = arg
- elif opt in ('-i', '--ifile'):
- self.in_file = arg
- elif opt in ('-o', '--ofile'):
- self.out_file = arg
- elif opt in ('-d', '--img_id'):
- self.img_id = arg
- elif opt in ('-c', '--policy'):
- self.policy = arg
- elif opt in ('-m', '--core'):
- self.set_core = True
-
- if len(self.in_file) == 0 or len(self.out_file) == 0:
- print(usage, file=sys.stderr)
- sys.exit(1)
-
-
-class AreaList:
- """List of flash areas"""
-
- def __init__(self, plat, flash, use_overwrite):
- self.plat = plat
- self.flash = flash
- self.use_overwrite = use_overwrite
- self.areas = []
- self.peers = {}
- self.trailers = {}
- self.internal_flash = False
- self.external_flash = False
- self.external_flash_xip = False
-
- def get_min_erase_size(self):
- """Calculate minimum erase block size for int./ext. Flash """
- return self.plat['eraseSize'] if self.plat['flashSize'] > 0 \
- else self.flash['eraseSize']
-
- def get_img_trailer_size(self):
- """Calculate image trailer size"""
- return self.get_min_erase_size()
-
- def process_int_area(self, title, fa_addr, fa_size,
- img_trailer_size, shared_slot):
- """Process internal flash area"""
- fa_device_id = 'FLASH_DEVICE_INTERNAL_FLASH'
- fa_off = fa_addr - self.plat['flashAddr']
- if img_trailer_size is not None:
- if self.use_overwrite:
- if shared_slot:
- print('Shared slot', title,
- 'is not supported in OVERWRITE mode',
- file=sys.stderr)
- sys.exit(7)
- else:
- # Check trailer alignment (start at the sector boundary)
- align = (fa_off + fa_size - img_trailer_size) % \
- self.plat['eraseSize']
- if align != 0:
- fa_addr += self.plat['eraseSize'] - align
- if fa_addr + fa_size <= \
- self.plat['flashAddr'] + self.plat['flashSize']:
- print('Misaligned', title,
- '- suggested address', hex(fa_addr),
- file=sys.stderr)
- else:
- print('Misaligned', title, file=sys.stderr)
- sys.exit(7)
- else:
- # Check alignment (flash area should start at the sector boundary)
- if fa_off % self.plat['eraseSize'] != 0:
- print('Misaligned', title, file=sys.stderr)
- sys.exit(7)
- slot_sectors = int((fa_off % self.plat['eraseSize'] +
- fa_size + self.plat['eraseSize'] - 1) //
- self.plat['eraseSize'])
- return fa_device_id, fa_off, slot_sectors
-
- def process_ext_area(self, title, fa_addr, fa_size,
- img_trailer_size, shared_slot):
- """Process external flash area"""
- if self.flash is None:
- print('Unspecified SPI Flash IC',
- file=sys.stderr)
- sys.exit(3)
- if fa_addr + fa_size <= \
- self.plat['smifAddr'] + self.flash['flashSize']:
- flash_idx = 'CY_BOOT_EXTERNAL_DEVICE_INDEX'
- fa_device_id = f'FLASH_DEVICE_EXTERNAL_FLASH({flash_idx})'
- fa_off = fa_addr - self.plat['smifAddr']
- else:
- print('Misfitting', title, file=sys.stderr)
- sys.exit(7)
- if img_trailer_size is not None:
- if self.use_overwrite:
- if shared_slot:
- print('Shared slot', title,
- 'is not supported in OVERWRITE mode',
- file=sys.stderr)
- sys.exit(7)
- else:
- # Check trailer alignment (start at the sector boundary)
- align = (fa_off + fa_size - img_trailer_size) % \
- self.flash['eraseSize']
- if align != 0:
- peer_addr = self.peers.get(fa_addr)
- if shared_slot:
- # Special case when using both int. and ext. memory
- if self.plat['flashSize'] > 0 and \
- align % self.plat['eraseSize'] == 0:
- print('Note:', title, 'requires', align,
- 'padding bytes before trailer',
- file=sys.stderr)
- else:
- print('Misaligned', title, file=sys.stderr)
- sys.exit(7)
- elif is_same_mem(fa_addr, peer_addr) and \
- fa_addr % self.flash['eraseSize'] == \
- peer_addr % self.flash['eraseSize']:
- pass # postpone checking
- else:
- fa_addr += self.flash['eraseSize'] - align
- if fa_addr + fa_size <= \
- self.plat['smifAddr'] + self.flash['flashSize']:
- print('Misaligned', title,
- '- suggested address', hex(fa_addr),
- file=sys.stderr)
- else:
- print('Misaligned', title, file=sys.stderr)
- sys.exit(7)
- else:
- # Check alignment (flash area should start at the sector boundary)
- if fa_off % self.flash['eraseSize'] != 0:
- print('Misaligned', title, file=sys.stderr)
- sys.exit(7)
- slot_sectors = int((fa_off % self.flash['eraseSize'] +
- fa_size + self.flash['eraseSize'] - 1) //
- self.flash['eraseSize'])
- self.external_flash = True
- if self.flash['XIP']:
- self.external_flash_xip = True
- return fa_device_id, fa_off, slot_sectors
-
- def chk_area(self, fa_addr, fa_size, peer_addr=None):
- """Check area location (internal/external flash)"""
- if peer_addr is not None:
- self.peers[peer_addr] = fa_addr
- fa_limit = fa_addr + fa_size
- if self.plat['flashSize'] and \
- fa_addr >= self.plat['flashAddr'] and \
- fa_limit <= self.plat['flashAddr'] + self.plat['flashSize']:
- # Internal flash
- self.internal_flash = True
-
- def add_area(self, title,
- fa_id, fa_addr, fa_size,
- img_trailer_size=None, shared_slot=False):
- """Add flash area to AreaList.
- Internal/external flash is detected by address.
- Returns number of sectors in a slot"""
- if fa_size == 0:
- print('Empty', title, file=sys.stderr)
- sys.exit(7)
-
- fa_limit = fa_addr + fa_size
- if self.plat['flashSize'] and \
- fa_addr >= self.plat['flashAddr'] and \
- fa_limit <= self.plat['flashAddr'] + self.plat['flashSize']:
- # Internal flash
- fa_device_id, fa_off, slot_sectors = self.process_int_area(
- title, fa_addr, fa_size, img_trailer_size, shared_slot)
- align = self.plat['eraseSize']
- elif self.plat['smifSize'] and \
- fa_addr >= self.plat['smifAddr'] and \
- fa_limit <= self.plat['smifAddr'] + self.plat['smifSize']:
- # External flash
- fa_device_id, fa_off, slot_sectors = self.process_ext_area(
- title, fa_addr, fa_size, img_trailer_size, shared_slot)
- align = self.flash['eraseSize']
- else:
- print('Invalid', title, file=sys.stderr)
- sys.exit(7)
-
- if shared_slot:
- assert img_trailer_size is not None
- tr_addr = fa_addr + fa_size - img_trailer_size
- tr_name = self.trailers.get(tr_addr)
- if tr_name is not None:
- print('Same trailer address for', title, 'and', tr_name,
- file=sys.stderr)
- sys.exit(7)
- self.trailers[tr_addr] = title
-
- # Ensure no flash areas on this device will overlap, except the
- # shared slot
- for area in self.areas:
- if fa_device_id == area['fa_device_id']:
- over = is_overlap(fa_off, fa_size,
- area['fa_off'], area['fa_size'],
- align)
- if shared_slot and area['shared_slot']:
- if not over: # images in shared slot should overlap
- print(title, 'is not shared with', area['title'],
- file=sys.stderr)
- sys.exit(7)
- elif over:
- print(title, 'overlaps with', area['title'],
- file=sys.stderr)
- sys.exit(7)
-
- self.areas.append({'title': title,
- 'shared_slot': shared_slot,
- 'fa_id': fa_id,
- 'fa_device_id': fa_device_id,
- 'fa_off': fa_off,
- 'fa_size': fa_size})
- return slot_sectors
-
- def generate_c_source(self, params):
- """Generate C source"""
- c_array = 'flash_areas'
-
- try:
- with open(params.out_file, "w", encoding='UTF-8') as out_f:
- out_f.write('/* AUTO-GENERATED FILE, DO NOT EDIT.'
- ' ALL CHANGES WILL BE LOST! */\n')
- out_f.write(f'/* Platform: {params.plat_id} */\n')
- out_f.write("#ifndef CY_FLASH_MAP_H\n")
- out_f.write("#define CY_FLASH_MAP_H\n\n")
-
- if self.plat.get('bitsPerCnt'):
- out_f.write('#ifdef NEED_FLASH_MAP\n')
- out_f.write(f'static struct flash_area {c_array}[] = {{\n')
- comma = len(self.areas)
- area_count = 0
- for area in self.areas:
- comma -= 1
- if area['fa_id'] is not None:
- sss = ' /* Shared secondary slot */' \
- if area['shared_slot'] else ''
- out_f.writelines('\n'.join([
- ' {' + sss,
- f" .fa_id = {area['fa_id']},",
- f" .fa_device_id = {area['fa_device_id']},",
- f" .fa_off = {hex(area['fa_off'])}U,",
- f" .fa_size = {hex(area['fa_size'])}U",
- ' },' if comma else ' }', '']))
- area_count += 1
- out_f.write('};\n\n'
- 'struct flash_area *boot_area_descs[] = {\n')
- for area_index in range(area_count):
- out_f.write(f' &{c_array}[{area_index}U],\n')
- out_f.write(' NULL\n};\n')
-
- if self.plat.get('bitsPerCnt'):
- out_f.write('#endif /* NEED_FLASH_MAP */\n')
- out_f.close()
-
- # inserted here to fix misra 'header guard'
- list_counters = process_policy_20829(params.policy)
- if list_counters is not None:
- form_max_counter_array(list_counters, params.out_file)
- with open(params.out_file, "a", encoding='UTF-8') as out_f:
- out_f.write("#endif /* CY_FLASH_MAP_H */\n")
- else:
- out_f.write("#endif /* CY_FLASH_MAP_H */\n")
-
- except (FileNotFoundError, OSError):
- print('Cannot create', params.out_file, file=sys.stderr)
- sys.exit(4)
-
-
-def cvt_dec_or_hex(val, desc):
- """Convert (hexa)decimal string to number"""
- try:
- return int(val, 0)
- except ValueError:
- print('Invalid value', val, 'for', desc, file=sys.stderr)
- sys.exit(6)
-
-
-def get_val(obj, attr):
- """Get JSON 'value'"""
- obj = obj[attr]
- try:
- return cvt_dec_or_hex(obj['value'], obj['description'])
- except KeyError as key:
- print('Malformed JSON:', key,
- 'is missing in', "'" + attr + "'",
- file=sys.stderr)
- sys.exit(5)
-
-
-def get_bool(obj, attr, def_val=False):
- """Get JSON boolean value (returns def_val if it is missing)"""
- ret_val = def_val
- obj = obj.get(attr)
- if obj is not None:
- try:
- val = str(obj['value']).lower()
- desc = obj['description']
- if val == 'true':
- ret_val = True
- elif val == 'false':
- ret_val = False
- else:
- print('Invalid value', val, 'for', desc, file=sys.stderr)
- sys.exit(6)
- except KeyError as key:
- print('Malformed JSON:', key,
- 'is missing in', "'" + attr + "'",
- file=sys.stderr)
- sys.exit(5)
- return ret_val
-
-
-def get_str(obj, attr, def_val=None):
- """Get JSON string value (returns def_val if it is missing)"""
- ret_val = def_val
- obj = obj.get(attr)
- if obj is not None:
- try:
- ret_val = str(obj['value'])
- except KeyError as key:
- print('Malformed JSON:', key,
- 'is missing in', "'" + attr + "'",
- file=sys.stderr)
- sys.exit(5)
- return ret_val
-
-
-class AddrSize:
- """Bootloader area"""
-
- def __init__(self, bootloader, addr_name, size_name):
- self.fa_addr = get_val(bootloader, addr_name)
- self.fa_size = get_val(bootloader, size_name)
-
-
-def calc_status_size(boot_swap_status_row_sz, max_img_sectors,
- img_number, scratch_flag=True):
- """Estimate status size, see swap_status.h"""
- boot_swap_status_cnt_sz = 4
- boot_swap_status_crc_sz = 4
- boot_swap_status_mgcrec_sz = 4
- boot_swap_status_trailer_size = 64
- boot_swap_status_payld_sz = \
- boot_swap_status_row_sz - boot_swap_status_mgcrec_sz - \
- boot_swap_status_cnt_sz - boot_swap_status_crc_sz
- boot_swap_status_sect_rows_num = \
- int((max_img_sectors - 1) //
- boot_swap_status_payld_sz) + 1
- boot_swap_status_trail_rows_num = \
- int((boot_swap_status_trailer_size - 1) //
- boot_swap_status_payld_sz) + 1
- boot_swap_status_d_size = \
- boot_swap_status_row_sz * \
- (boot_swap_status_sect_rows_num + boot_swap_status_trail_rows_num)
- boot_swap_status_mult = 2
- boot_swap_status_size = boot_swap_status_mult * boot_swap_status_d_size
- status_zone_cnt = 2 * img_number
- if scratch_flag:
- status_zone_cnt += 1
- return boot_swap_status_size * status_zone_cnt
-
-
-def process_json(in_file):
- """Process JSON"""
- try:
- with open(in_file, encoding='UTF-8') as in_f:
- try:
- flash_map = json.load(in_f)
- except ValueError:
- print('Cannot parse', in_file, file=sys.stderr)
- sys.exit(4)
- except (FileNotFoundError, OSError):
- print('Cannot open', in_file, file=sys.stderr)
- sys.exit(4)
- flash = flash_map.get('external_flash')
- if flash is not None:
- flash = flash[0]
- model = flash.get('model')
- mode = flash.get('mode')
- if model is not None:
- try:
- flash = flashDict[model]
- except KeyError:
- print('Supported SPI Flash ICs are:',
- ', '.join(flashDict.keys()),
- file=sys.stderr)
- sys.exit(3)
- else:
- try:
- flash = {'flashSize': cvt_dec_or_hex(flash['flash-size'],
- 'flash-size'),
- 'eraseSize': cvt_dec_or_hex(flash['erase-size'],
- 'erase-size')}
- except KeyError as key:
- print('Malformed JSON:', key,
- "is missing in 'external_flash'",
- file=sys.stderr)
- sys.exit(3)
- flash.update({'XIP': str(mode).upper() == 'XIP'})
- return flash_map['boot_and_upgrade'], flash
-
-
-def process_images(area_list, boot_and_upgrade):
- """Process images"""
- app_count = 0
- slot_sectors_max = 0
- all_shared = get_bool(boot_and_upgrade['bootloader'], 'shared_slot')
- any_shared = all_shared
- app_core = None
- apps_flash_map = [None, ]
-
- for stage in range(2):
- for app_index in range(1, 5):
-
- app_flash_map = {}
-
- try:
- app_ident = f'application_{app_index}'
- application = boot_and_upgrade[app_ident]
- try:
- primary_addr = get_val(application, 'address')
- primary_size = get_val(application, 'size')
- secondary_addr = get_val(application, 'upgrade_address')
- secondary_size = get_val(application, 'upgrade_size')
- except KeyError as key:
- print('Malformed JSON:', key, 'is missing',
- file=sys.stderr)
- sys.exit(5)
- if stage == 0:
- if primary_size != secondary_size:
- print('Primary and secondary slot sizes'
- ' are different for', app_ident,
- file=sys.stderr)
- sys.exit(6)
- area_list.chk_area(primary_addr, primary_size)
- area_list.chk_area(secondary_addr, secondary_size,
- primary_addr)
- if application.get('core') is None:
- if app_index == 1:
- app_core = area_list.plat['appCore']
- elif app_index > 1:
- print('"core" makes sense only for the 1st app',
- file=sys.stderr)
- sys.exit(6)
- else:
- app_core = get_str(application, 'core',
- area_list.plat['appCore'])
- if app_index == 1:
- app_core = area_list.plat['allCores'].get(app_core.lower())
- if app_core is None:
- print('Unknown "core"', file=sys.stderr)
- sys.exit(6)
- else:
- slot_sectors_max = max(
- slot_sectors_max,
- area_list.add_area(
- f'{app_ident} (primary slot)',
- f'FLASH_AREA_IMG_{app_index}_PRIMARY',
- primary_addr, primary_size,
- area_list.get_img_trailer_size()))
- shared_slot = get_bool(application, 'shared_slot', all_shared)
- any_shared = any_shared or shared_slot
- slot_sectors_max = max(
- slot_sectors_max,
- area_list.add_area(
- f'{app_ident} (secondary slot)',
- f'FLASH_AREA_IMG_{app_index}_SECONDARY',
- secondary_addr, secondary_size,
- area_list.get_img_trailer_size(),
- shared_slot))
-
- app_slot_prim = {"address": hex(primary_addr), "size": hex(primary_size)}
- app_slot_sec = {"address": hex(secondary_addr), "size": hex(secondary_size)}
-
- app_flash_map.update({"primary": app_slot_prim, "secondary": app_slot_sec})
- apps_flash_map.append(app_flash_map)
-
- app_count = app_index
-
- except KeyError:
- break
- if app_count == 0:
- print('Malformed JSON: no application(s) found',
- file=sys.stderr)
- sys.exit(5)
-
- return app_core, app_count, slot_sectors_max, apps_flash_map, any_shared
-
-def process_policy_20829(in_policy):
- """Process policy file to get data of NV-counter"""
- list_counters = None
-
- try:
- with open(in_policy, encoding='UTF-8') as in_f:
- try:
- policy = json.load(in_f)
- except ValueError:
- print('\nERROR: Cannot parse', in_policy,'\n', file=sys.stderr)
- sys.exit(4)
- finally:
- in_f.close()
- except (FileNotFoundError, OSError):
- print('Cannot open', in_policy, file=sys.stderr)
- sys.exit(4)
-
- try:
- nv_cnt = policy["device_policy"]['reprovisioning']['nv_counter']
- list_values = nv_cnt["value"]
- list_counters = nv_cnt["bits_per_cnt"]
- except KeyError:
- print("\nERROR: Check path to 'nv_counter' and its correctness in policy file", in_policy,
- ".\n", file=sys.stderr)
- sys.exit(2)
-
- #Check correctness of NV-counter
- try:
- len_list_value = len(list_values)
- len_list_counters = len(list_counters)
- except TypeError:
- print("\nERROR: Fields 'value' and 'bits_per_cnt' of 'nv_counter' in policy file",
- in_policy,"must be arrays.\n", file=sys.stderr)
- sys.exit(2)
-
- if len_list_value != len_list_counters:
- print("\nERROR: Fields 'value' and 'bits_per_cnt' of 'nv_counter' in policy file",
- in_policy,"must have the same size.\n", file=sys.stderr)
- sys.exit(2)
-
- sum_all_counters = 0
- for i in range(len_list_value):
- sum_all_counters += list_counters[i]
- if list_values[i] > list_counters[i]:
- print("\nERROR: Field 'value' cannot be more then 'bits_per_cnt'.", file=sys.stderr)
- print("Check 'nv_counter' in policy file", in_policy,"\n", file=sys.stderr)
- sys.exit(2)
-
- sum_all_bit_nv_counter = 32
- if sum_all_counters != sum_all_bit_nv_counter:
- print("\nERROR: The sum of all 'bits_per_cnt' must be equal to 32.", file=sys.stderr)
- print("Check 'nv_counter' in policy file", in_policy,"\n", file=sys.stderr)
- sys.exit(2)
-
- return list_counters
-
-
-def form_max_counter_array(in_list, out_file):
- '''Write bit_per_count array to output file
- There is expected, that "out_file" already exists'''
-
- out_array_str = "\n#ifdef NEED_MAX_COUNTERS\nstatic const uint8_t bits_per_cnt[] = {"
-
- #in_list is checked in prior function 'process_policy()'
- for i, list_member in enumerate(in_list):
- out_array_str += str(list_member)
- if i < len(in_list) - 1:
- out_array_str += ", "
- out_array_str += "};\n#endif\n"
-
- try:
- with open(out_file, "a", encoding='UTF-8') as out_f:
- out_f.write(out_array_str)
- except (FileNotFoundError, OSError):
- print('\nERROR: Cannot open ', out_file, file=sys.stderr)
- sys.exit(7)
-
-
-def main():
- """Flash map converter"""
- params = CmdLineParams()
-
- try:
- plat = platDict[params.plat_id]
- except KeyError:
- print('Supported platforms are:', ', '.join(platDict.keys()),
- file=sys.stderr)
- sys.exit(2)
-
- try:
- boot_and_upgrade, flash = process_json(params.in_file)
- bootloader = boot_and_upgrade['bootloader']
- boot = AddrSize(bootloader, 'address', 'size')
- except KeyError as key:
- print('Malformed JSON:', key, 'is missing',
- file=sys.stderr)
- sys.exit(5)
-
- try:
- scratch = AddrSize(bootloader, 'scratch_address', 'scratch_size')
- except KeyError:
- scratch = None
-
- try:
- swap_status = AddrSize(bootloader, 'status_address', 'status_size')
- except KeyError:
- swap_status = None
-
- # Create flash areas
- area_list = AreaList(plat, flash, scratch is None and swap_status is None)
- area_list.add_area('bootloader', 'FLASH_AREA_BOOTLOADER',
- boot.fa_addr, boot.fa_size)
-
- # Service RAM app (optional)
- service_app = boot_and_upgrade.get('service_app')
- app_binary = None
- input_params = None
- app_desc = None
- if service_app is not None:
- if plat['flashSize'] > 0:
- print('service_app is unsupported on this platform',
- file=sys.stderr)
- sys.exit(7)
- try:
- app_binary = AddrSize(service_app, 'address', 'size')
- input_params = AddrSize(service_app, 'params_address', 'params_size')
- app_desc = AddrSize(service_app, 'desc_address', 'desc_size')
- if input_params.fa_addr != app_binary.fa_addr + app_binary.fa_size or \
- app_desc.fa_addr != input_params.fa_addr + input_params.fa_size or \
- app_desc.fa_size != 0x20:
- print('Malformed service_app definition', file=sys.stderr)
- sys.exit(7)
- area_list.add_area('service_app', None, app_binary.fa_addr,
- app_binary.fa_size + input_params.fa_size + app_desc.fa_size)
- except KeyError as key:
- print('Malformed JSON:', key, 'is missing',
- file=sys.stderr)
- sys.exit(5)
-
- # Fill flash areas
- app_core, app_count, slot_sectors_max, apps_flash_map, shared_slot = \
- process_images(area_list, boot_and_upgrade)
-
- cy_img_hdr_size = 0x400
- app_start = int(apps_flash_map[1].get("primary").get("address"), 0) + cy_img_hdr_size
-
- if app_start % plat['VTAlign'] != 0:
- print('Starting address', apps_flash_map[1].get("primary").get("address"),
- '+', hex(cy_img_hdr_size),
- 'must be aligned to', hex(plat['VTAlign']),
- file=sys.stderr)
- sys.exit(7)
-
- slot_sectors_max = max(slot_sectors_max, 32)
-
- if swap_status is not None:
- status_size_min = calc_status_size(area_list.get_min_erase_size(),
- slot_sectors_max,
- app_count,
- scratch is not None)
-
- if swap_status.fa_size < status_size_min:
- print('Insufficient swap status area - suggested size',
- hex(status_size_min),
- file=sys.stderr)
- sys.exit(7)
- area_list.add_area('swap status partition',
- 'FLASH_AREA_IMAGE_SWAP_STATUS',
- swap_status.fa_addr, swap_status.fa_size)
-
- if scratch is not None:
- area_list.add_area('scratch area',
- 'FLASH_AREA_IMAGE_SCRATCH',
- scratch.fa_addr, scratch.fa_size)
-
- # Compare size 'bit_per_cnt' and number of images.
- # 'service_app' is used only when HW rollback counter exists
- if plat.get('bitsPerCnt') is not None and service_app is not None:
- plat['bitsPerCnt'] = True
- list_counters = process_policy_20829(params.policy)
- if list_counters is not None and len(list_counters) != app_count:
- print("\nERROR: 'bits_per_cnt' must be present for each image!",
- file=sys.stderr)
- print("Please, check secure provisioning and reprovisioning policies.\n",
- file=sys.stderr)
- sys.exit(7)
-
-
- # Image id parameter is not used for MCUBootApp
- if params.img_id is None:
- area_list.generate_c_source(params)
-
- # Report necessary values back to make
- print('# AUTO-GENERATED FILE, DO NOT EDIT. ALL CHANGES WILL BE LOST!')
- print('BOOTLOADER_SIZE :=', hex(boot.fa_size))
- if params.set_core:
- print('CORE :=', plat['allCores'][plat['bootCore'].lower()])
- print('APP_CORE :=', app_core)
-
- if params.img_id is not None:
- primary_img_start = apps_flash_map[int(params.img_id)].get("primary").get("address")
- secondary_img_start = apps_flash_map[int(params.img_id)].get("secondary").get("address")
- slot_size = apps_flash_map[int(params.img_id)].get("primary").get("size")
-
- print('PRIMARY_IMG_START := ' + primary_img_start)
- print('SECONDARY_IMG_START := ' + secondary_img_start)
- print('SLOT_SIZE := ' + slot_size)
- else:
- print('MCUBOOT_IMAGE_NUMBER :=', app_count)
- print('MAX_IMG_SECTORS :=', slot_sectors_max)
-
- if area_list.use_overwrite:
- print('USE_OVERWRITE := 1')
- if area_list.external_flash:
- print('USE_EXTERNAL_FLASH := 1')
- if area_list.external_flash_xip:
- print('USE_XIP := 1')
- if shared_slot:
- print('USE_SHARED_SLOT := 1')
- if service_app is not None:
- print('PLATFORM_SERVICE_APP_OFFSET :=',
- hex(app_binary.fa_addr - plat['smifAddr']))
- print('PLATFORM_SERVICE_APP_INPUT_PARAMS_OFFSET :=',
- hex(input_params.fa_addr - plat['smifAddr']))
- print('PLATFORM_SERVICE_APP_DESC_OFFSET :=',
- hex(app_desc.fa_addr - plat['smifAddr']))
- print('USE_HW_ROLLBACK_PROT := 1')
-
-
-if __name__ == '__main__':
- main()
diff --git a/boot/cypress/scripts/memorymap.py b/boot/cypress/scripts/memorymap.py
new file mode 100644
index 0000000..5511c9a
--- /dev/null
+++ b/boot/cypress/scripts/memorymap.py
@@ -0,0 +1,1434 @@
+"""MCUBoot Flash Map Converter (JSON to .h)
+Copyright (c) 2022 Infineon Technologies AG
+"""
+
+import sys
+import getopt
+import json
+from enum import Enum
+import os.path
+
+class Error(Enum):
+ ''' Application error codes '''
+ ARG = 1
+ POLICY = 2
+ FLASH = 3
+ IO = 4
+ JSON = 5
+ VALUE = 6
+ CONFIG_MISMATCH = 7
+
+SERVICE_APP_SZ = 0x20
+
+c_array = 'flash_areas'
+
+# Supported Platforms
+cm0pCore = {
+ 'cortex-m0+': 'CM0P',
+ 'cm0+': 'CM0P',
+ 'm0+': 'CM0P',
+ 'cortex-m0p': 'CM0P',
+ 'cm0p': 'CM0P',
+ 'm0p': 'CM0P',
+ 'cortex-m0plus': 'CM0P',
+ 'cm0plus': 'CM0P',
+ 'm0plus': 'CM0P'
+}
+
+cm4Core = {
+ 'cortex-m4': 'CM4',
+ 'cm4': 'CM4',
+ 'm4': 'CM4'
+}
+
+cm33Core = {
+ 'cortex-m33': 'CM33',
+ 'cm33': 'CM33',
+ 'm33': 'CM33'
+}
+
+cm7Core = {
+ 'cortex-m7': 'CM7',
+ 'cm7': 'CM7',
+ 'm7': 'CM7'
+}
+
+allCores_PSOC_06x = {**cm0pCore, **cm4Core}
+
+common_PSOC_061 = {
+ 'flashAddr': 0x10000000,
+ 'eraseSize': 0x200, # 512 bytes
+ 'smifAddr': 0x18000000,
+ 'smifSize': 0x8000000, # i.e., window size
+ 'VTAlign': 0x400, # Vector Table alignment
+ 'allCores': cm4Core,
+ 'bootCore': 'Cortex-M4',
+ 'appCore': 'Cortex-M4'
+}
+
+common_PSOC_06x = {
+ 'flashAddr': 0x10000000,
+ 'eraseSize': 0x200, # 512 bytes
+ 'smifAddr': 0x18000000,
+ 'smifSize': 0x8000000, # i.e., window size
+ 'VTAlign': 0x400, # Vector Table alignment
+ 'allCores': allCores_PSOC_06x,
+ 'bootCore': 'Cortex-M0+',
+ 'appCore': 'Cortex-M4'
+}
+
+common_XMC7000 = {
+ 'flashAddr': 0x10000000,
+ 'eraseSize': 0x8000, # 512 bytes
+ 'smifAddr': 0x18000000,
+ 'smifSize': 0x8000000, # i.e., window size
+ 'VTAlign': 0x400, # Vector Table alignment
+ 'allCores': cm7Core,
+ 'bootCore': 'Cortex-M7',
+ 'appCore': 'Cortex-M7'
+}
+
+common_PSE84 = {
+ 'flashAddr': 0x32000000,
+ 'flashSize': 0x40000,
+ 'eraseSize': 0x20, # 32 bytes
+ 'smifAddr': 0x60000000, #secure address
+ 'smifSize': 0x4000000, # i.e., window size
+ 'VTAlign': 0x400, # Vector Table alignment
+ 'allCores': cm33Core,
+ 'bootCore': 'Cortex-M33',
+ 'appCore': 'Cortex-M33'
+}
+
+platDict = {
+ 'PSOC_061_2M': {
+ 'flashSize': 0x200000, # 2 MBytes
+ **common_PSOC_061
+ },
+ 'PSOC_061_1M': {
+ 'flashSize': 0x100000, # 1 MByte
+ **common_PSOC_061
+ },
+ 'PSOC_061_512K': {
+ 'flashSize': 0x80000, # 512 KBytes
+ **common_PSOC_061
+ },
+
+ 'PSOC_062_2M': {
+ 'flashSize': 0x200000, # 2 MBytes
+ **common_PSOC_06x
+ },
+ 'PSOC_062_1M': {
+ 'flashSize': 0x100000, # 1 MByte
+ **common_PSOC_06x
+ },
+ 'PSOC_062_512K': {
+ 'flashSize': 0x80000, # 512 KBytes
+ **common_PSOC_06x
+ },
+
+ 'PSOC_063_1M': {
+ 'flashSize': 0x100000, # 1 MByte
+ **common_PSOC_06x
+ },
+
+ 'XMC7200': {
+ 'flashSize': 0x100000, # 1 MByte
+ **common_XMC7000
+ },
+
+ 'XMC7100': {
+ 'flashSize': 0x100000, # 1 MByte
+ **common_PSOC_06x
+ },
+
+ 'CYW20829': {
+ 'flashSize': 0, # n/a
+ 'smifAddr': 0x60000000,
+ 'smifSize': 0x8000000, # i.e., window size
+ 'VTAlign': 0x200, # Vector Table alignment
+ 'allCores': cm33Core,
+ 'bootCore': 'Cortex-M33',
+ 'appCore': 'Cortex-M33',
+ 'bitsPerCnt': False
+ },
+
+ 'PSE84_L4': {
+ **common_PSE84
+ },
+
+ 'PSE84_L2': {
+ **common_PSE84
+ }
+}
+
+# Supported SPI Flash ICs
+flashDict = {
+ # Fudan
+ 'FM25Q04': {
+ 'flashSize': 0x80000, # 4 Mbits
+ 'eraseSize': 0x1000, # 128 uniform sectors with 4K-byte each
+ },
+ 'FM25W04': {
+ 'flashSize': 0x80000, # 4 Mbits
+ 'eraseSize': 0x1000, # 128 uniform sectors with 4K-byte each
+ },
+ 'FM25Q08': {
+ 'flashSize': 0x100000, # 8 Mbits
+ 'eraseSize': 0x1000, # 256 uniform sectors with 4K-byte each
+ },
+ 'FM25W08': {
+ 'flashSize': 0x100000, # 8 Mbits
+ 'eraseSize': 0x1000, # 256 uniform sectors with 4K-byte each
+ },
+ # Puya
+ 'P25Q05H': {
+ 'flashSize': 0x10000, # 512 Kbits
+ 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
+ },
+ 'P25Q10H': {
+ 'flashSize': 0x20000, # 1 Mbit
+ 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
+ },
+ 'P25Q20H': {
+ 'flashSize': 0x40000, # 2 Mbits
+ 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
+ },
+ 'P25Q40H': {
+ 'flashSize': 0x80000, # 4 Mbits
+ 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
+ },
+ # Infineon
+ 'S25HS256T': {
+ 'flashSize': 0x2000000, # 256 Mbits
+ 'eraseSize': 0x40000, # Uniform Sector Architecture
+ },
+ 'S25HS512T': {
+ 'flashSize': 0x4000000, # 512 Mbits
+ 'eraseSize': 0x40000, # Uniform Sector Architecture
+ },
+ 'S25HS01GT': {
+ 'flashSize': 0x8000000, # 1 Gbit
+ 'eraseSize': 0x40000, # Uniform Sector Architecture
+ }
+}
+
+
+def is_overlap(fa1off, fa1size, fa2off, fa2size, align):
+ """Check if two flash areas on the same device overlap"""
+ mask = align - 1
+ assert align > 0 and (align & mask) == 0 # ensure align is a power of 2
+ fa1end = (fa1off + fa1size + mask) & ~mask
+ fa2end = (fa2off + fa2size + mask) & ~mask
+ fa1off = fa1off & ~mask
+ fa2off = fa2off & ~mask
+ return fa1off < fa2end and fa2off < fa1end
+
+
+def is_same_mem(fa1addr, fa2addr):
+ """Check if two addresses belong to the same memory"""
+ if fa1addr is None or fa2addr is None:
+ return False
+ mask = 0xFF000000
+ return (fa1addr & mask) == (fa2addr & mask)
+
+
+class CmdLineParams:
+ """Command line parameters"""
+
+ def __init__(self):
+ self.plat_id = ''
+ self.in_file = ''
+ self.out_file = ''
+ self.fa_file = ''
+ self.img_id = None
+ self.policy = None
+ self.set_core = False
+ self.image_boot_config = False
+
+ usage = 'USAGE:\n' + sys.argv[0] + \
+ ''' -p <platform> -i <flash_map.json> -o <memorymap.c> -a <memorymap.h> -d <img_id> -c <policy.json>
+
+OPTIONS:
+-h --help Display the usage information
+-p --platform= Target (e.g., PSOC_062_512K)
+-i --ifile= JSON flash map file
+-o --ofile= C file to be generated
+-a --fa_file= path where to create 'memorymap.h'
+-d --img_id ID of application to build
+-c --policy Policy file in JSON format
+-m --core Detect and set Cortex-M CORE
+-x --image_boot_config Generate image boot config structure
+'''
+
+ try:
+ opts, unused = getopt.getopt(
+ sys.argv[1:], 'hi:o:a:p:d:c:x:m',
+ ['help', 'platform=', 'ifile=', 'ofile=', "fa_file=", 'img_id=', 'policy=', 'core', 'image_boot_config'])
+ except getopt.GetoptError:
+ print(usage, file=sys.stderr)
+ sys.exit(Error.ARG)
+
+ for opt, arg in opts:
+ if opt in ('-h', '--help'):
+ print(usage, file=sys.stderr)
+ sys.exit()
+ elif opt in ('-p', '--platform'):
+ self.plat_id = arg
+ elif opt in ('-i', '--ifile'):
+ self.in_file = arg
+ elif opt in ('-o', '--ofile'):
+ self.out_file = arg
+ elif opt in ('-a', '--fa_file'):
+ self.fa_file = arg
+ elif opt in ('-d', '--img_id'):
+ self.img_id = arg
+ elif opt in ('-c', '--policy'):
+ self.policy = arg
+ elif opt in ('-m', '--core'):
+ self.set_core = True
+ elif opt in ('x', '--image_boot_config'):
+ self.image_boot_config = True
+
+ if len(self.in_file) == 0 or len(self.out_file) == 0 or len(self.fa_file) == 0:
+ print(usage, file=sys.stderr)
+ sys.exit(Error.ARG)
+
+
+class AreaList:
+ '''
+ A List of flash areas
+ ...
+
+ Attributes
+ ----------
+ plat : dict
+ Platform settings
+
+ flash : dict
+ External flash settings
+
+ use_overwrite : bool
+ Overwrite configuration in use
+
+ areas : list
+ Flash area parameter list
+
+ peers : set
+ Peers
+
+ trailers : set
+ Flash area trailers
+
+ internal_flash : bool
+ Internal flash in use
+
+ external_flash : bool
+ External flash in use
+
+ external_flash_xip : bool
+ External XIP in use
+
+ Methods
+ -------
+ get_min_erase_size:
+ Calculate minimum erase block size for int./ext. Flash
+
+ get_img_trailer_size:
+ Calculate image trailer size
+
+ process_int_area:
+ Process internal flash area
+
+ process_ext_area:
+ Process external flash area
+
+ chk_area:
+ Check area location (internal/external flash)
+
+ add_area:
+ Add flash area to AreaList.
+ Internal/external flash is detected by address.
+
+ generate_c_source:
+ Generate C source
+
+ create_flash_area_id:
+ Creates flash_area_id.h file.
+ '''
+
+ def __init__(self, plat, flash, use_overwrite):
+ self.plat = plat
+ self.flash = flash
+ self.use_overwrite = use_overwrite
+ self.areas = []
+ self.peers = {}
+ self.trailers = {}
+ self.internal_flash = False
+ self.external_flash = False
+ self.external_flash_xip = False
+
+ def get_min_erase_size(self):
+ '''Calculate minimum erase block size for int./ext. Flash '''
+ return self.plat['eraseSize'] if self.plat['flashSize'] > 0 \
+ else self.flash['eraseSize']
+
+ def get_img_trailer_size(self):
+ '''Calculate image trailer size'''
+ return self.get_min_erase_size()
+
+ def process_int_area(self, title, addr, fa_size,
+ img_trailer_size, shared_slot):
+ '''
+ Process internal flash area
+ Parameters:
+ ----------
+ title : str
+ Area name
+
+ addr : int
+ Area address
+
+ fa_size : int
+ Area size
+
+ img_trailer_size : int
+ Trailer size
+
+ shared_slot : bool
+ Shared slot option in use
+
+ Returns:
+ ----------
+ fa_device_id : str
+
+ fa_off : int
+
+ slot_sectors : int
+ '''
+ fa_device_id = 'FLASH_DEVICE_INTERNAL_FLASH'
+ fa_off = addr - self.plat['flashAddr']
+ if img_trailer_size is not None:
+ if self.use_overwrite:
+ if shared_slot:
+ print('Shared slot', title,
+ 'is not supported in OVERWRITE mode',
+ file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ else:
+ # Check trailer alignment (start at the sector boundary)
+ align = (fa_off + fa_size - img_trailer_size) % \
+ self.plat['eraseSize']
+ if align != 0:
+ addr += self.plat['eraseSize'] - align
+ if addr + fa_size <= \
+ self.plat['flashAddr'] + self.plat['flashSize']:
+ print('Misaligned', title,
+ '- suggested address', hex(addr),
+ file=sys.stderr)
+ else:
+ print('Misaligned', title, file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ else:
+ # Check alignment (flash area should start at the sector boundary)
+ if fa_off % self.plat['eraseSize'] != 0:
+ print('Misaligned', title, file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ slot_sectors = int((fa_off % self.plat['eraseSize'] +
+ fa_size + self.plat['eraseSize'] - 1) //
+ self.plat['eraseSize'])
+ return fa_device_id, fa_off, slot_sectors
+
+ def process_ext_area(self, title, addr, fa_size,
+ img_trailer_size, shared_slot):
+ '''
+ Process external flash area
+ Parameters:
+ ----------
+ title : str
+ Area name
+
+ addr : int
+ Area address
+
+ fa_size : int
+ Area size
+
+ img_trailer_size : int
+ Trailer size
+
+ shared_slot : bool
+ Shared slot option in use
+
+ Returns:
+ ----------
+ fa_device_id : str
+
+ fa_off : int
+
+ slot_sectors : int
+ '''
+ if self.flash is None:
+ print('Unspecified SPI Flash IC',
+ file=sys.stderr)
+ sys.exit(Error.FLASH)
+ if addr + fa_size <= \
+ self.plat['smifAddr'] + self.flash['flashSize']:
+ flash_idx = 'CY_BOOT_EXTERNAL_DEVICE_INDEX'
+ fa_device_id = f'FLASH_DEVICE_EXTERNAL_FLASH({flash_idx})'
+ fa_off = addr - self.plat['smifAddr']
+ else:
+ print('Misfitting', title, file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ if img_trailer_size is not None:
+ if self.use_overwrite:
+ if shared_slot:
+ print('Shared slot', title,
+ 'is not supported in OVERWRITE mode',
+ file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ else:
+ # Check trailer alignment (start at the sector boundary)
+ align = (fa_off + fa_size - img_trailer_size) % \
+ self.flash['eraseSize']
+ if align != 0:
+ peer_addr = self.peers.get(addr)
+ if shared_slot:
+ # Special case when using both int. and ext. memory
+ if self.plat['flashSize'] > 0 and \
+ align % self.plat['eraseSize'] == 0:
+ print('Note:', title, 'requires', align,
+ 'padding bytes before trailer',
+ file=sys.stderr)
+ else:
+ print('Misaligned', title, file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ elif is_same_mem(addr, peer_addr) and \
+ addr % self.flash['eraseSize'] == \
+ peer_addr % self.flash['eraseSize']:
+ pass # postpone checking
+ else:
+ addr += self.flash['eraseSize'] - align
+ if addr + fa_size <= \
+ self.plat['smifAddr'] + self.flash['flashSize']:
+ print('Misaligned', title,
+ '- suggested address', hex(addr),
+ file=sys.stderr)
+ else:
+ print('Misaligned', title, file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ else:
+ # Check alignment (flash area should start at the sector boundary)
+ if fa_off % self.flash['eraseSize'] != 0:
+ print('Misaligned', title, file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ slot_sectors = int((fa_off % self.flash['eraseSize'] +
+ fa_size + self.flash['eraseSize'] - 1) //
+ self.flash['eraseSize'])
+ self.external_flash = True
+ if self.flash['XIP']:
+ self.external_flash_xip = True
+ return fa_device_id, fa_off, slot_sectors
+
+ def chk_area(self, addr, fa_size, peer_addr=None):
+ '''
+ Check area location (internal/external flash)
+ Parameters:
+ ----------
+ addr : int
+ Area address
+
+ fa_size : int
+ Area size
+
+ peer_addr : bool (optional)
+ Shared slot option in use
+
+ Returns:
+ ----------
+ None
+ '''
+ if peer_addr is not None:
+ self.peers[peer_addr] = addr
+ fa_limit = addr + fa_size
+ if self.plat['flashSize'] and \
+ addr >= self.plat['flashAddr'] and \
+ fa_limit <= self.plat['flashAddr'] + self.plat['flashSize']:
+ # Internal flash
+ self.internal_flash = True
+
+ def add_area(self, title,
+ fa_id, addr, fa_size,
+ img_trailer_size=None, shared_slot=False):
+ '''
+ Add flash area to AreaList.
+ Internal/external flash is detected by address.
+ Parameters:
+ ----------
+ title : str
+ Area name
+
+ fa_id : str
+ Area id
+
+ addr : int
+ Area address
+
+ fa_size : int
+ Area size
+
+ img_trailer_size : int
+ Trailer size (optional)
+
+ shared_slot : bool
+ Shared slot option in use (optional)
+
+ Returns:
+ ----------
+ slot_sectors : int
+ Number of sectors in a slot
+ '''
+ if fa_size == 0:
+ print('Empty', title, file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+
+ fa_limit = addr + fa_size
+ if self.plat['flashSize'] and \
+ addr >= self.plat['flashAddr'] and \
+ fa_limit <= self.plat['flashAddr'] + self.plat['flashSize']:
+ # Internal flash
+ fa_device_id, fa_off, slot_sectors = self.process_int_area(
+ title, addr, fa_size, img_trailer_size, shared_slot)
+ align = self.plat['eraseSize']
+ elif self.plat['smifSize'] and \
+ addr >= self.plat['smifAddr'] and \
+ fa_limit <= self.plat['smifAddr'] + self.plat['smifSize']:
+ # External flash
+ fa_device_id, fa_off, slot_sectors = self.process_ext_area(
+ title, addr, fa_size, img_trailer_size, shared_slot)
+ align = self.flash['eraseSize']
+ else:
+ print('Invalid', title, file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+
+ if shared_slot:
+ assert img_trailer_size is not None
+ tr_addr = addr + fa_size - img_trailer_size
+ tr_name = self.trailers.get(tr_addr)
+ if tr_name is not None:
+ print('Same trailer address for', title, 'and', tr_name,
+ file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ self.trailers[tr_addr] = title
+
+ # Ensure no flash areas on this device will overlap, except the
+ # shared slot
+ for area in self.areas:
+ if fa_device_id == area['fa_device_id']:
+ over = is_overlap(fa_off, fa_size,
+ area['fa_off'], area['fa_size'],
+ align)
+ if shared_slot and area['shared_slot']:
+ if not over: # images in shared slot should overlap
+ print(title, 'is not shared with', area['title'],
+ file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ elif over:
+ print(title, 'overlaps with', area['title'],
+ file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+
+ self.areas.append({'title': title,
+ 'shared_slot': shared_slot,
+ 'fa_id': fa_id,
+ 'fa_device_id': fa_device_id,
+ 'fa_off': fa_off,
+ 'fa_size': fa_size})
+ return slot_sectors
+
+ def generate_c_source(self, params):
+ '''
+ Generate C source
+ Parameters:
+ ----------
+ params : CmdLineParams
+ Application parameters
+
+ Returns:
+ ----------
+ None
+ '''
+ c_array = 'flash_areas'
+
+ try:
+ with open(params.out_file, "w", encoding='UTF-8') as out_f:
+
+ out_f.write(f'#include "{params.fa_file}"\n')
+ out_f.write(f'#include "flash_map_backend.h"\n\n')
+ out_f.write(f'#include "flash_map_backend_platform.h"\n\n')
+ out_f.write(f'struct flash_area {c_array}[] = {{\n')
+ comma = len(self.areas)
+ area_count = 0
+ for area in self.areas:
+ comma -= 1
+ if area['fa_id'] is not None:
+ sss = ' /* Shared secondary slot */' \
+ if area['shared_slot'] else ''
+ out_f.writelines('\n'.join([
+ ' {' + sss,
+ f" .fa_id = {area['fa_id']},",
+ f" .fa_device_id = {area['fa_device_id']},",
+ f" .fa_off = {hex(area['fa_off'])}U,",
+ f" .fa_size = {hex(area['fa_size'])}U",
+ ' },' if comma else ' }', '']))
+ area_count += 1
+ out_f.write('};\n\n'
+ 'struct flash_area *boot_area_descs[] = {\n')
+ for area_index in range(area_count):
+ out_f.write(f' &{c_array}[{area_index}U],\n')
+ out_f.write(' NULL\n};\n')
+ out_f.close()
+
+ except (FileNotFoundError, OSError):
+ print('Cannot create', params.out_file, file=sys.stderr)
+ sys.exit(Error.IO)
+
+ def create_flash_area_id(self, img_number, params):
+ """ Get 'img_number' and generate flash_area_id.h file' """
+
+ #check if params.fa_file already exists and it has FLASH_AREA_ID
+ if os.path.exists(params.fa_file):
+ with open(params.fa_file, "r", encoding='UTF-8') as fa_f:
+ content = fa_f.read()
+ res = content.find(f"FLASH_AREA_IMG_{img_number}_SECONDARY")
+ if res != -1:
+ fa_f.close()
+ return
+
+ fa_f.close()
+
+ try:
+ with open(params.fa_file, "w", encoding='UTF-8') as fa_f:
+ fa_f.write("#ifndef MEMORYMAP_H\n")
+ fa_f.write("#define MEMORYMAP_H\n\n")
+ fa_f.write('/* AUTO-GENERATED FILE, DO NOT EDIT.'
+ ' ALL CHANGES WILL BE LOST! */\n')
+ fa_f.write(f'#include "flash_map_backend.h"\n\n')
+
+ fa_f.write(f'extern struct flash_area {c_array}[];\n')
+ fa_f.write(f'extern struct flash_area *boot_area_descs[];\n')
+
+ #we always have BOOTLOADER and IMG_1_
+ fa_f.write("#define FLASH_AREA_BOOTLOADER ( 0u)\n\n")
+ fa_f.write("#define FLASH_AREA_IMG_1_PRIMARY ( 1u)\n")
+ fa_f.write("#define FLASH_AREA_IMG_1_SECONDARY ( 2u)\n\n")
+
+ fa_f.write("#define FLASH_AREA_IMAGE_SCRATCH ( 3u)\n")
+ fa_f.write("#define FLASH_AREA_IMAGE_SWAP_STATUS ( 7u)\n\n")
+
+ for img in range(2, img_number + 1):
+ """ img_id_primary and img_id_secondary must be aligned with the
+ flash_area_id, calculated in the functions
+ __STATIC_INLINE uint8_t FLASH_AREA_IMAGE_PRIMARY(uint32_t img_idx) and
+ __STATIC_INLINE uint8_t FLASH_AREA_IMAGE_SECONDARY(uint32_t img_idx),
+ in boot/cypress/platforms/memory/sysflash/sysflash.h
+ """
+
+ slots_for_image = 2
+ img_id_primary = None
+ img_id_secondary = None
+
+ if img == 2:
+ img_id_primary = int(slots_for_image * img)
+ img_id_secondary = int(slots_for_image * img + 1)
+
+ #number 7 is used for FLASH_AREA_IMAGE_SWAP_STATUS, so the next is 8
+ if img >= 3:
+ img_id_primary = int(slots_for_image * img + 2)
+ img_id_secondary = int(slots_for_image * img + 3)
+
+ fa_f.write(f"#define FLASH_AREA_IMG_{img}_PRIMARY ( {img_id_primary}u)\n")
+ fa_f.write(f"#define FLASH_AREA_IMG_{img}_SECONDARY ( {img_id_secondary}u)\n\n")
+
+ if self.plat.get('bitsPerCnt'):
+ fa_f.close()
+
+ list_counters = process_policy_20829(params.policy)
+ if list_counters is not None:
+ form_max_counter_array(list_counters, params.fa_file)
+ else:
+ fa_f.write("#endif /* MEMORYMAP_H */")
+ fa_f.close()
+
+ except (FileNotFoundError, OSError):
+ print('\nERROR: Cannot create ', params.fa_file, file=sys.stderr)
+ sys.exit(Error.IO)
+
+
+def cvt_dec_or_hex(val, desc):
+ """Convert (hexa)decimal string to number"""
+ try:
+ return int(val, 0)
+ except ValueError:
+ print('Invalid value', val, 'for', desc, file=sys.stderr)
+ sys.exit(Error.VALUE)
+
+
+def get_val(obj, attr):
+ """Get JSON 'value'"""
+ obj = obj[attr]
+ try:
+ return cvt_dec_or_hex(obj['value'], obj['description'])
+ except KeyError as key:
+ print('Malformed JSON:', key,
+ 'is missing in', "'" + attr + "'",
+ file=sys.stderr)
+ sys.exit(Error.JSON)
+
+
+def get_bool(obj, attr, def_val=False):
+ """Get JSON boolean value (returns def_val if it is missing)"""
+ ret_val = def_val
+ obj = obj.get(attr)
+ if obj is not None:
+ try:
+ val = str(obj['value']).lower()
+ desc = obj['description']
+ if val == 'true':
+ ret_val = True
+ elif val == 'false':
+ ret_val = False
+ else:
+ print('Invalid value', val, 'for', desc, file=sys.stderr)
+ sys.exit(Error.VALUE)
+ except KeyError as key:
+ print('Malformed JSON:', key,
+ 'is missing in', "'" + attr + "'",
+ file=sys.stderr)
+ sys.exit(Error.JSON)
+ return ret_val
+
+
+def get_str(obj, attr, def_val=None):
+ """Get JSON string value (returns def_val if it is missing)"""
+ ret_val = def_val
+ obj = obj.get(attr)
+ if obj is not None:
+ try:
+ ret_val = str(obj['value'])
+ except KeyError as key:
+ print('Malformed JSON:', key,
+ 'is missing in', "'" + attr + "'",
+ file=sys.stderr)
+ sys.exit(Error.JSON)
+ return ret_val
+
+
+class AddrSize:
+ """Bootloader area"""
+
+ def __init__(self, bootloader, addr_name, size_name):
+ self.addr = get_val(bootloader, addr_name)
+ self.size = get_val(bootloader, size_name)
+
+
+def calc_status_size(boot_swap_status_row_sz, max_img_sectors,
+ img_number, scratch_flag=True):
+ """Estimate status size, see swap_status.h"""
+ boot_swap_status_cnt_sz = 4
+ boot_swap_status_crc_sz = 4
+ boot_swap_status_mgcrec_sz = 4
+ boot_swap_status_trailer_size = 64
+ boot_swap_status_payld_sz = \
+ boot_swap_status_row_sz - boot_swap_status_mgcrec_sz - \
+ boot_swap_status_cnt_sz - boot_swap_status_crc_sz
+ boot_swap_status_sect_rows_num = \
+ int((max_img_sectors - 1) //
+ boot_swap_status_payld_sz) + 1
+ boot_swap_status_trail_rows_num = \
+ int((boot_swap_status_trailer_size - 1) //
+ boot_swap_status_payld_sz) + 1
+ boot_swap_status_d_size = \
+ boot_swap_status_row_sz * \
+ (boot_swap_status_sect_rows_num + boot_swap_status_trail_rows_num)
+ boot_swap_status_mult = 2
+ boot_swap_status_size = boot_swap_status_mult * boot_swap_status_d_size
+ status_zone_cnt = 2 * img_number
+ if scratch_flag:
+ status_zone_cnt += 1
+ return boot_swap_status_size * status_zone_cnt
+
+
+def process_json(in_file):
+ """Process JSON"""
+ try:
+ with open(in_file, encoding='UTF-8') as in_f:
+ try:
+ flash_map = json.load(in_f)
+ except ValueError:
+ print('Cannot parse', in_file, file=sys.stderr)
+ sys.exit(Error.IO)
+ except (FileNotFoundError, OSError):
+ print('Cannot open', in_file, file=sys.stderr)
+ sys.exit(Error.IO)
+ flash = flash_map.get('external_flash')
+ if flash is not None:
+ flash = flash[0]
+ model = flash.get('model')
+ mode = flash.get('mode')
+ if model is not None:
+ try:
+ flash = flashDict[model]
+ except KeyError:
+ print('Supported SPI Flash ICs are:',
+ ', '.join(flashDict.keys()),
+ file=sys.stderr)
+ sys.exit(Error.FLASH)
+ else:
+ try:
+ flash = {'flashSize': cvt_dec_or_hex(flash['flash-size'],
+ 'flash-size'),
+ 'eraseSize': cvt_dec_or_hex(flash['erase-size'],
+ 'erase-size')}
+ except KeyError as key:
+ print('Malformed JSON:', key,
+ "is missing in 'external_flash'",
+ file=sys.stderr)
+ sys.exit(Error.FLASH)
+ flash.update({'XIP': str(mode).upper() == 'XIP'})
+ return flash_map.get('boot_and_upgrade', None), flash_map.get('ram_app_staging', None), flash
+
+def process_boot_type(boot_and_upgrade):
+ image_boot_mode = []
+
+ for app_index in range(1, 5):
+ app_ident = f'application_{app_index}'
+ application = boot_and_upgrade.get(app_ident)
+
+ if application:
+ ram = application.get('ram', application.get('ram_boot'))
+
+ if ram:
+ image_boot_mode.append(
+ {
+ 'mode': 'IMAGE_BOOT_MODE_FLASH' if application.get('ram') else 'IMAGE_BOOT_MODE_RAM',
+ 'address': ram.get('address', {}).get('value', 0),
+ 'size': ram.get('size', {}).get('value', 0),
+ }
+ )
+
+ return image_boot_mode
+
+def generate_boot_type(image_boot_mode):
+ c_file = "image_boot_config.c"
+ h_file = "image_boot_config.h"
+ try:
+ with open(c_file, "w", encoding='UTF-8') as out_f:
+ out_f.write('/* AUTO-GENERATED FILE, DO NOT EDIT.'
+ ' ALL CHANGES WILL BE LOST! */\n')
+
+ out_f.write(f'#include "{h_file}"\n')
+ out_f.write('\nimage_boot_config_t image_boot_config[BOOT_IMAGE_NUMBER] = {\n')
+ for mode in image_boot_mode:
+ out_f.writelines('\n'.join([
+ '\t{\n'
+ f"\t\t.mode = {mode['mode']},",
+ f"\t\t.address = {mode['address']},",
+ f"\t\t.size = {mode['size']},",
+ '\t},\n']))
+ out_f.write('};\n')
+
+ with open(h_file, "w", encoding='UTF-8') as out_f:
+ out_f.write('/* AUTO-GENERATED FILE, DO NOT EDIT.'
+ ' ALL CHANGES WILL BE LOST! */\n')
+ out_f.write('#ifndef IMAGE_BOOT_CONFIG_H\n')
+ out_f.write('#define IMAGE_BOOT_CONFIG_H\n')
+ out_f.write('#include "bootutil/bootutil.h"\n')
+ out_f.writelines('\n'.join([
+ ' ',
+ 'typedef enum',
+ '{',
+ '\tIMAGE_BOOT_MODE_FLASH = 0U,',
+ '\tIMAGE_BOOT_MODE_RAM = 1U,',
+ '} image_boot_mode_t;',
+ '',
+ 'typedef struct image_boot_config_s {',
+ '\timage_boot_mode_t mode;',
+ '\tuint32_t address;',
+ '\tuint32_t size;',
+ '} image_boot_config_t;',
+ '',
+ 'extern image_boot_config_t image_boot_config[BOOT_IMAGE_NUMBER];'
+ ]))
+ out_f.write('\n#endif /* IMAGE_BOOT_CONFIG_H */\n')
+
+ except (FileNotFoundError, OSError):
+ print('Cannot create', out_f, file=sys.stderr)
+ sys.exit(Error.IO)
+
+
+def process_images(area_list, boot_and_upgrade):
+ """Process images"""
+ app_count = 0
+ slot_sectors_max = 0
+ all_shared = get_bool(boot_and_upgrade['bootloader'], 'shared_slot')
+ any_shared = all_shared
+ app_core = None
+ apps_flash_map = [None, ]
+ apps_ram_map = [None, ]
+
+ for stage in range(2):
+ for app_index in range(1, 5):
+
+ app_flash_map = {}
+ app_ram_map = {}
+ app_ram_boot = False
+
+ try:
+ app_ident = f'application_{app_index}'
+ application = boot_and_upgrade[app_ident]
+
+ try:
+ flash = application['flash']
+ except KeyError:
+ #Backward compatibility
+ flash = application
+
+ try:
+ config = application['config']
+ except KeyError:
+ #Backward compatibility
+ config = application
+
+ try:
+ ram = application['ram']
+ except KeyError:
+ try:
+ ram = application['ram_boot']
+ app_ram_boot = True
+ except KeyError:
+ ram = None
+
+ try:
+ primary_addr = get_val(flash, 'address')
+ primary_size = get_val(flash, 'size')
+ secondary_addr = get_val(flash, 'upgrade_address')
+ secondary_size = get_val(flash, 'upgrade_size')
+
+ if ram is not None:
+ app_ram_addr = get_val(ram, 'address')
+ app_ram_size = get_val(ram, 'size')
+ else:
+ app_ram_addr = None
+ app_ram_size = None
+
+ except KeyError as key:
+ print('Malformed JSON:', key, 'is missing',
+ file=sys.stderr)
+ sys.exit(Error.JSON)
+ if stage == 0:
+ if primary_size != secondary_size:
+ print('Primary and secondary slot sizes'
+ ' are different for', app_ident,
+ file=sys.stderr)
+ sys.exit(Error.VALUE)
+ area_list.chk_area(primary_addr, primary_size)
+ area_list.chk_area(secondary_addr, secondary_size,
+ primary_addr)
+ if config is None or config.get('core') is None:
+ if app_index == 1:
+ app_core = area_list.plat['appCore']
+ elif app_index > 1:
+ print('"core" makes sense only for the 1st app',
+ file=sys.stderr)
+ sys.exit(Error.VALUE)
+ else:
+ app_core = get_str(config, 'core',
+ area_list.plat['appCore'])
+ if app_index == 1:
+ app_core = area_list.plat['allCores'].get(app_core.lower())
+ if app_core is None:
+ print('Unknown "core"', file=sys.stderr)
+ sys.exit(Error.VALUE)
+ else:
+ slot_sectors_max = max(
+ slot_sectors_max,
+ area_list.add_area(
+ f'{app_ident} (primary slot)',
+ f'FLASH_AREA_IMG_{app_index}_PRIMARY',
+ primary_addr, primary_size,
+ area_list.get_img_trailer_size()))
+ shared_slot = get_bool(flash, 'shared_slot', all_shared)
+ any_shared = any_shared or shared_slot
+ slot_sectors_max = max(
+ slot_sectors_max,
+ area_list.add_area(
+ f'{app_ident} (secondary slot)',
+ f'FLASH_AREA_IMG_{app_index}_SECONDARY',
+ secondary_addr, secondary_size,
+ area_list.get_img_trailer_size(),
+ shared_slot))
+
+ app_slot_prim = {"address": hex(primary_addr), "size": hex(primary_size)}
+ app_slot_sec = {"address": hex(secondary_addr), "size": hex(secondary_size)}
+
+ app_flash_map.update({"primary": app_slot_prim, "secondary": app_slot_sec})
+ apps_flash_map.append(app_flash_map)
+
+ if ram is not None:
+ app_ram_map.update({"address": app_ram_addr
+ , "size": app_ram_size
+ , "ram_boot": app_ram_boot})
+ apps_ram_map.append(app_ram_map)
+ else:
+ apps_ram_map = None
+
+ app_count = app_index
+
+ except KeyError:
+ break
+ if app_count == 0:
+ print('Malformed JSON: no application(s) found',
+ file=sys.stderr)
+ sys.exit(Error.JSON)
+
+ return app_core, app_count, slot_sectors_max, apps_flash_map, apps_ram_map, any_shared
+
+
+def process_policy_20829(in_policy):
+ """Process policy file to get data of NV-counter"""
+ list_counters = None
+
+ try:
+ with open(in_policy, encoding='UTF-8') as in_f:
+ try:
+ policy = json.load(in_f)
+ except ValueError:
+ print('\nERROR: Cannot parse', in_policy,'\n', file=sys.stderr)
+ sys.exit(Error.IO)
+ finally:
+ in_f.close()
+ except (FileNotFoundError, OSError):
+ print('Cannot open', in_policy, file=sys.stderr)
+ sys.exit(Error.IO)
+
+ try:
+ nv_cnt = policy["device_policy"]['reprovisioning']['nv_counter']
+ list_values = nv_cnt["value"]
+ list_counters = nv_cnt["bits_per_cnt"]
+ except KeyError:
+ print("\nERROR: Check path to 'nv_counter' and its correctness in policy file", in_policy,
+ ".\n", file=sys.stderr)
+ sys.exit(Error.POLICY)
+
+ #Check correctness of NV-counter
+ try:
+ len_list_value = len(list_values)
+ len_list_counters = len(list_counters)
+ except TypeError:
+ print("\nERROR: Fields 'value' and 'bits_per_cnt' of 'nv_counter' in policy file",
+ in_policy,"must be arrays.\n", file=sys.stderr)
+ sys.exit(Error.POLICY)
+
+ if len_list_value != len_list_counters:
+ print("\nERROR: Fields 'value' and 'bits_per_cnt' of 'nv_counter' in policy file",
+ in_policy,"must have the same size.\n", file=sys.stderr)
+ sys.exit(Error.POLICY)
+
+ sum_all_counters = 0
+ for i in range(len_list_value):
+ sum_all_counters += list_counters[i]
+ if list_values[i] > list_counters[i]:
+ print("\nERROR: Field 'value' cannot be more then 'bits_per_cnt'.", file=sys.stderr)
+ print("Check 'nv_counter' in policy file", in_policy,"\n", file=sys.stderr)
+ sys.exit(Error.POLICY)
+
+ sum_all_bit_nv_counter = 32
+ if sum_all_counters != sum_all_bit_nv_counter:
+ print("\nERROR: The sum of all 'bits_per_cnt' must be equal to 32.", file=sys.stderr)
+ print("Check 'nv_counter' in policy file", in_policy,"\n", file=sys.stderr)
+ sys.exit(Error.POLICY)
+
+ return list_counters
+
+
+def form_max_counter_array(in_list, out_file):
+ '''Write bit_per_count array to output file
+ There is expected, that "out_file" already exists'''
+
+ #ifdef here is needed to fix Rule 12.2 MISRA violation
+ out_array_str = "\n#ifdef NEED_MAX_COUNTERS\nstatic const uint8_t bits_per_cnt[] = {"
+
+ #in_list is checked in prior function 'process_policy()'
+ for i, list_member in enumerate(in_list):
+ out_array_str += str(list_member)
+ if i < len(in_list) - 1:
+ out_array_str += ", "
+ out_array_str += "};\n#endif\n"
+
+ try:
+ with open(out_file, "a", encoding='UTF-8') as out_f:
+ out_f.write(out_array_str)
+ out_f.write("\n#endif /* MEMORYMAP_H */")
+ except (FileNotFoundError, OSError):
+ print('\nERROR: Cannot open ', out_file, file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+
+
+def main():
+ """Flash map converter"""
+ params = CmdLineParams()
+
+ try:
+ plat = platDict[params.plat_id]
+ except KeyError:
+ print('Supported platforms are:', ', '.join(platDict.keys()),
+ file=sys.stderr)
+ sys.exit(Error.POLICY)
+
+ try:
+ boot_and_upgrade, ram_app_staging, flash = process_json(params.in_file)
+ bootloader = boot_and_upgrade['bootloader']
+ try:
+ bootloader_config = bootloader['config']
+ except KeyError:
+ #Backward compatibility
+ bootloader_config = bootloader
+
+ if ram_app_staging is not None:
+ try:
+ ram_app_staging_size = get_val(ram_app_staging, 'size')
+ ram_app_staging_ext_mem_addr = get_val(ram_app_staging, 'external_mememory_address')
+ ram_app_staging_sram_stage_addr = get_val(ram_app_staging, 'sram_stage_address')
+ ram_app_staging_reset_trigger = get_bool(ram_app_staging, 'reset_after_staging')
+ except KeyError:
+ ram_app_staging = None
+
+ try:
+ bootloader_flash = bootloader['flash']
+ except KeyError:
+ #Backward compatibility
+ bootloader_flash = bootloader
+
+ try:
+ bootloader_ram = bootloader['ram']
+ except KeyError:
+ #Backward compatibility
+ bootloader_ram = bootloader
+
+ boot_flash_area = AddrSize(bootloader_flash, 'address', 'size')
+ boot_ram_area = AddrSize(bootloader_ram, 'address', 'size')
+ except KeyError as key:
+ print('Malformed JSON:', key, 'is missing',
+ file=sys.stderr)
+ sys.exit(Error.JSON)
+
+ try:
+ scratch = AddrSize(bootloader_flash, 'scratch_address', 'scratch_size')
+ except KeyError:
+ scratch = None
+
+ try:
+ swap_status = AddrSize(bootloader_flash, 'status_address', 'status_size')
+ except KeyError:
+ swap_status = None
+
+ try:
+ bootloader_shared_data = bootloader['shared_data']
+ boot_shared_data_area = AddrSize(bootloader_shared_data, 'address', 'size')
+ except KeyError:
+ boot_shared_data_area = None
+
+ try:
+ bootloader_startup = get_bool(bootloader_config, 'startup')
+ except KeyError:
+ bootloader_startup = None
+
+ try:
+ ram_app_area = AddrSize(bootloader_config['ram_boot'], 'address', 'size')
+ except KeyError:
+ ram_app_area = None
+
+
+ # Create flash areas
+ area_list = AreaList(plat, flash, scratch is None and swap_status is None)
+ area_list.add_area('bootloader', 'FLASH_AREA_BOOTLOADER',
+ boot_flash_area.addr, boot_flash_area.size)
+
+ # Service RAM app (optional)
+ service_app = boot_and_upgrade.get('service_app')
+ app_binary = None
+ input_params = None
+ app_desc = None
+ if service_app is not None:
+ if plat['flashSize'] > 0:
+ print('service_app is unsupported on this platform',
+ file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ try:
+ app_binary = AddrSize(service_app, 'address', 'size')
+ input_params = AddrSize(service_app, 'params_address', 'params_size')
+ app_desc = AddrSize(service_app, 'desc_address', 'desc_size')
+ if input_params.addr != app_binary.addr + app_binary.size or \
+ app_desc.addr != input_params.addr + input_params.size or \
+ app_desc.size != SERVICE_APP_SZ:
+ print('Malformed service_app definition', file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ area_list.add_area('service_app', None, app_binary.addr,
+ app_binary.size + input_params.size + app_desc.size)
+ except KeyError as key:
+ print('Malformed JSON:', key, 'is missing',
+ file=sys.stderr)
+ sys.exit(Error.JSON)
+
+ # Fill flash areas
+ app_core, app_count, slot_sectors_max, apps_flash_map, apps_ram_map, shared_slot = \
+ process_images(area_list, boot_and_upgrade)
+
+ if params.image_boot_config:
+ image_boot_mode = process_boot_type(boot_and_upgrade)
+
+ if image_boot_mode:
+ generate_boot_type(image_boot_mode)
+
+ cy_img_hdr_size = 0x400
+ app_start = int(apps_flash_map[1].get("primary").get("address"), 0) + cy_img_hdr_size
+
+ if app_start % plat['VTAlign'] != 0:
+ print('Starting address', apps_flash_map[1].get("primary").get("address"),
+ '+', hex(cy_img_hdr_size),
+ 'must be aligned to', hex(plat['VTAlign']),
+ file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+
+ slot_sectors_max = max(slot_sectors_max, 32)
+
+ if swap_status is not None:
+ status_size_min = calc_status_size(area_list.get_min_erase_size(),
+ slot_sectors_max,
+ app_count,
+ scratch is not None)
+
+ if swap_status.size < status_size_min:
+ print('Insufficient swap status area - suggested size',
+ hex(status_size_min),
+ file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+ area_list.add_area('swap status partition',
+ 'FLASH_AREA_IMAGE_SWAP_STATUS',
+ swap_status.addr, swap_status.size)
+
+ if scratch is not None:
+ area_list.add_area('scratch area',
+ 'FLASH_AREA_IMAGE_SCRATCH',
+ scratch.addr, scratch.size)
+
+ # Compare size 'bit_per_cnt' and number of images.
+ # 'service_app' is used only when HW rollback counter exists
+ if plat.get('bitsPerCnt') is not None and service_app is not None:
+ plat['bitsPerCnt'] = True
+ list_counters = process_policy_20829(params.policy)
+ if list_counters is not None and len(list_counters) != app_count:
+ print("\nERROR: 'bits_per_cnt' must be present for each image!",
+ file=sys.stderr)
+ print("Please, check secure provisioning and reprovisioning policies.\n",
+ file=sys.stderr)
+ sys.exit(Error.CONFIG_MISMATCH)
+
+ # Image id parameter is not used for MCUBootApp
+ if params.img_id is None:
+ area_list.generate_c_source(params)
+
+ area_list.create_flash_area_id(app_count, params)
+
+ # Report necessary values back to make
+ print('# AUTO-GENERATED FILE, DO NOT EDIT. ALL CHANGES WILL BE LOST!')
+ if params.set_core:
+ print('CORE :=', plat['allCores'][plat['bootCore'].lower()])
+
+ if ram_app_staging is not None:
+ print('USE_STAGE_RAM_APPS := 1')
+ print('RAM_APP_STAGING_EXT_MEM_ADDR := ', hex(ram_app_staging_ext_mem_addr))
+ print('RAM_APP_STAGING_SRAM_MEM_ADDR :=', hex(ram_app_staging_sram_stage_addr))
+ print('RAM_APP_STAGING_SIZE := ', hex(ram_app_staging_size))
+ if ram_app_staging_reset_trigger is True:
+ print('RAM_APP_RESET_TRIGGER := 1')
+
+ if bootloader_startup is True:
+ print('BOOTLOADER_STARTUP := 1')
+
+ if ram_app_area is not None:
+ print('USE_MCUBOOT_RAM_LOAD := 1')
+ print('IMAGE_EXECUTABLE_RAM_START :=', hex(ram_app_area.addr))
+ print('IMAGE_EXECUTABLE_RAM_SIZE :=', hex(ram_app_area.size))
+
+ if boot_shared_data_area is not None:
+ print('USE_MEASURED_BOOT := 1')
+ print('USE_DATA_SHARING := 1')
+ print('BOOT_SHARED_DATA_ADDRESS :=', hex(boot_shared_data_area.addr)+'U')
+ print('BOOT_SHARED_DATA_SIZE :=', hex(boot_shared_data_area.size)+'U')
+ print('BOOT_SHARED_DATA_RECORD_SIZE :=', hex(boot_shared_data_area.size)+'U')
+
+ print('BOOTLOADER_ORIGIN :=', hex(boot_flash_area.addr))
+ print('BOOTLOADER_SIZE :=', hex(boot_flash_area.size))
+ print('BOOTLOADER_RAM_ORIGIN :=', hex(boot_ram_area.addr))
+ print('BOOTLOADER_RAM_SIZE :=', hex(boot_ram_area.size))
+ print('APP_CORE :=', app_core)
+
+ if params.img_id is not None:
+ primary_img_start = apps_flash_map[int(params.img_id)].get("primary").get("address")
+ secondary_img_start = apps_flash_map[int(params.img_id)].get("secondary").get("address")
+ slot_size = apps_flash_map[int(params.img_id)].get("primary").get("size")
+
+ if apps_ram_map:
+ image_ram_address = apps_ram_map[int(params.img_id)].get("address")
+ image_ram_size = apps_ram_map[int(params.img_id)].get("size")
+ image_ram_boot = apps_ram_map[int(params.img_id)].get("ram_boot")
+ if image_ram_address and image_ram_size:
+ print('IMG_RAM_ORIGIN := ' + hex(image_ram_address))
+ print('IMG_RAM_SIZE := ' + hex(image_ram_size))
+ if image_ram_boot is True:
+ print('USE_MCUBOOT_RAM_LOAD := 1')
+
+ print('PRIMARY_IMG_START := ' + primary_img_start)
+ print('SECONDARY_IMG_START := ' + secondary_img_start)
+ print('SLOT_SIZE := ' + slot_size)
+ else:
+ if apps_ram_map:
+ ram_load_counter = 0
+ for img in apps_ram_map:
+ if img is not None and img.get("ram_boot"):
+ ram_load_counter += 1
+
+ if ram_load_counter != 0:
+ if ram_load_counter == 1 and app_count == 1:
+ print('USE_MCUBOOT_RAM_LOAD := 1')
+ print(f'IMAGE_EXECUTABLE_RAM_START := {hex(apps_ram_map[1].get("address"))}')
+ print(f'IMAGE_EXECUTABLE_RAM_SIZE := {hex(apps_ram_map[1].get("size"))}')
+ else:
+ print('USE_MCUBOOT_MULTI_MEMORY_LOAD := 1')
+
+ print('MAX_IMG_SECTORS :=', slot_sectors_max)
+
+ print('MCUBOOT_IMAGE_NUMBER :=', app_count)
+ if area_list.external_flash:
+ print('USE_EXTERNAL_FLASH := 1')
+ if area_list.external_flash_xip:
+ print('USE_XIP := 1')
+
+ if area_list.use_overwrite:
+ print('USE_OVERWRITE := 1')
+ if shared_slot:
+ print('USE_SHARED_SLOT := 1')
+ if service_app is not None:
+ print('PLATFORM_SERVICE_APP_OFFSET :=',
+ hex(app_binary.addr - plat['smifAddr']))
+ print('PLATFORM_SERVICE_APP_INPUT_PARAMS_OFFSET :=',
+ hex(input_params.addr - plat['smifAddr']))
+ print('PLATFORM_SERVICE_APP_DESC_OFFSET :=',
+ hex(app_desc.addr - plat['smifAddr']))
+ print('USE_HW_ROLLBACK_PROT := 1')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/boot/cypress/scripts/memorymap_rework.py b/boot/cypress/scripts/memorymap_rework.py
new file mode 100644
index 0000000..47e681e
--- /dev/null
+++ b/boot/cypress/scripts/memorymap_rework.py
@@ -0,0 +1,582 @@
+"""
+Copyright 2023 Cypress Semiconductor Corporation (an Infineon company)
+or an affiliate of Cypress Semiconductor Corporation. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import sys
+import json
+import click
+
+APP_LIMIT = 8
+
+settings_dict = {
+ 'overwrite' : 'USE_OVERWRITE'
+ , 'swap' : 'USE_SWAP'
+ , 'status' : 'USE_STATUS'
+ , 'scratch' : 'USE_SCRATCH'
+ , 'measured_boot' : 'USE_MEASURED_BOOT'
+ , 'data_sharing' : 'USE_DATA_SHARING'
+ , 'ram_load' : 'USE_MCUBOOT_RAM_LOAD'
+ , 'multi_memory_load' : 'USE_MCUBOOT_MULTI_MEMORY_LOAD'
+ , 'shared_slot' : 'USE_SHARED_SLOT'
+ , 'ram_load_address' : 'IMAGE_EXECUTABLE_RAM_START'
+ , 'ram_load_size' : 'IMAGE_EXECUTABLE_RAM_SIZE'
+ , 'shared_data_address' : 'BOOT_SHARED_DATA_ADDRESS'
+ , 'shared_data_size' : 'BOOT_SHARED_DATA_SIZE'
+ , 'shared_data_record_size' : 'BOOT_SHARED_DATA_RECORD_SIZE'
+ , 'bootloader_app_address' : 'BOOTLOADER_ORIGIN'
+ , 'bootloader_app_size' : 'BOOTLOADER_SIZE'
+ , 'bootloader_ram_address' : 'BOOTLOADER_RAM_ORIGIN'
+ , 'bootloader_ram_size' : 'BOOTLOADER_RAM_SIZE'
+ , 'application_count' : 'MCUBOOT_IMAGE_NUMBER'
+ , 'boot_image' : 'BOOT_IMAGE_NUMBER'
+ , 'sectors_count' : 'MAX_IMG_SECTORS'
+ , 'core' : 'CORE'
+ , 'image_ram_address' : 'IMG_RAM_ORIGIN'
+ , 'image_ram_size' : 'IMG_RAM_SIZE'
+ , 'primary_image_start' : 'PRIMARY_IMG_START'
+ , 'secondary_image_start' : 'SECONDARY_IMG_START'
+ , 'image_size' : 'SLOT_SIZE'
+}
+
+def header_guard_generate(file):
+ file.write('/* AUTO-GENERATED FILE, DO NOT EDIT.'
+ ' ALL CHANGES WILL BE LOST! */\n')
+ file.write("#pragma once\n\n")
+
+def is_overlap(x : int, y : int) -> bool:
+ if x.start == x.stop or y.start == y.stop:
+ return False
+ return x.start < y.stop and y.start < x.stop
+
+def is_aligned(addr : int, sz : int) -> bool:
+ ''' Check address alignment '''
+ return addr % sz == 0
+
+class Memory:
+ ''' Memory handler '''
+ def __init__(self, addr, sz):
+ self.addr : int = addr
+ self.sz : int = sz
+
+ def overlaps_with(self, other) -> bool:
+ ''' Check Memory for intersection
+ @return Bool
+ '''
+ first = range(self.addr, self.addr + self.sz)
+ second = range(other.addr, other.addr + other.sz)
+
+ return is_overlap(first, second)
+
+ def fits_with(self, other) -> bool:
+ '''
+
+ '''
+ return \
+ self.addr >= other.addr and \
+ self.addr + self.sz <= other.addr + other.sz
+
+class MemoryRegion(Memory):
+ ''' Memory region handler '''
+ def __init__(self, addr, sz, erase_sz, erase_val, type):
+ super().__init__(addr, sz)
+ self.erase_sz : int = erase_sz
+ self.erase_val : int = erase_val
+ self.type = type
+
+class BootloaderLayout:
+ '''
+ Handler for bootloader memory layout
+ '''
+ def __init__(self):
+ self.bootloader_area : Memory = None
+ self.ram : Memory = None
+ self.scratch_area : Memory = None
+ self.status_area : Memory = None
+ self.shared_data : Memory = None
+ self.shared_upgrade : Memory = None
+ self.core_name : int = None
+
+ @property
+ def has_shared_upgrade(self) -> bool:
+ return self.shared_upgrade is not None
+
+ @property
+ def has_shared_data(self) -> bool:
+ return self.shared_data is not None
+
+ @property
+ def has_scratch_area(self) -> bool:
+ return self.scratch_area is not None
+
+ @property
+ def has_status_area(self) -> bool:
+ return self.status_area is not None
+
+ def parse(self, section : json):
+ '''
+ Parse JSON section and init fields.
+ '''
+ try:
+ fields = ('bootloader_area', 'scratch_area', 'status_area', \
+ 'shared_data', 'shared_upgrade', 'ram')
+ for field in fields:
+ area = section.get(field)
+ if area:
+ setattr(self, field, Memory(int(area['address'], 0),
+ int(area['size'], 0)))
+
+ core = section.get('core')
+ if core:
+ self.core_name = core
+
+ except KeyError as key:
+ print('Malformed JSON:', key, 'is missing')
+
+class MemoryAreaConfig:
+ '''
+ Handler for flash area configuration
+ '''
+ def __init__(self, area_name, device_name, offset, size):
+ self.fa_id : str = area_name
+ self.fa_device_id : str = device_name
+ self.fa_off : int = offset
+ self.fa_size : int = size
+
+class ApplicationLayout:
+ '''
+ Handler for application memory layout
+ '''
+ def __init__(self):
+ self.boot_area : Memory = None
+ self.upgrade_area : Memory = None
+ self.ram : Memory = None
+ self.ram_boot : Memory = None
+ self.core_name : str = None
+
+ @property
+ def has_ram_boot(self) -> bool:
+ return self.ram_boot is not None
+
+ @property
+ def has_upgrade_area(self) -> bool:
+ return self.upgrade_area is not None
+
+ def overlaps_with(self, other) -> bool:
+ return \
+ self.boot_area.overlaps_with(other.boot_area) or \
+ self.upgrade_area.overlaps_with(other.upgrade_area)
+
+ def parse(self, section : json):
+ '''
+ Parse JSON section and init fields.
+ '''
+ try:
+ slots = section['slots']
+ boot_address = int(slots['boot'], 0)
+ upgrade_address = int(slots['upgrade'], 0)
+ slot_size = int(slots['size'], 0)
+
+ self.boot_area = Memory(boot_address, slot_size)
+ self.upgrade_area = Memory(upgrade_address, slot_size)
+
+ fields = ('ram', 'ram_boot')
+ for field in fields:
+ area = section.get(field)
+ if area:
+ setattr(self, field, Memory(int(area['address'], 0),
+ int(area['size'], 0)))
+
+ core = section.get('core')
+ if core:
+ self.core_name = core
+
+ except KeyError as key:
+ print('Malformed JSON:', key, 'is missing')
+
+class MemoryMap:
+ '''
+ General handler
+ '''
+ def __init__(self):
+ self.boot_layout : BootloaderLayout = None
+ self.regions : MemoryRegion = []
+ self.region_types : str = []
+ self.apps : ApplicationLayout = []
+ self.primary_slots : str = []
+ self.secondary_slots: str = []
+ self.mem_areas : MemoryAreaConfig = []
+ self.param_dict = {}
+ self.map_json : json = None
+ self.platform_json : json = None
+ self.output_folder = None
+ self.output_name = None
+ self.max_sectors = 32
+
+ def __apps_init(self):
+ for image_number in range(1, APP_LIMIT):
+ app_ident = f'application_{image_number}'
+ section = self.map_json.get(app_ident)
+
+ if section:
+ app_layout = ApplicationLayout()
+ app_layout.parse(section)
+
+ self.apps.append(app_layout)
+ else:
+ break
+
+ def __boot_layout_init(self):
+ self.boot_layout = BootloaderLayout()
+ self.boot_layout.parse(self.map_json['bootloader'])
+
+ def __memory_regions_init(self):
+ memory_regions = self.platform_json['memory_regions']
+ for region in memory_regions:
+ try:
+ addr = int(region['address'], 0)
+ size = int(region['size'], 0)
+ erase_size = int(region['erase_size'], 0)
+ erase_value = int(region['erase_value'], 0)
+ type = str(region['type'])
+
+ if type not in self.region_types:
+ self.region_types.append(type)
+
+ self.regions.append(MemoryRegion(addr, size, erase_size, erase_value, type))
+ except KeyError as key:
+ print('Malformed JSON:', key, 'is missing')
+
+ # Check regions for overlap
+ for this in self.regions:
+ for other in self.regions:
+ if this is other:
+ continue
+ if this.overlaps_with(other):
+ # TODO: Notify regions overlap
+ raise Exception()
+
+ def __memory_area_find_region_id(self, area : Memory) -> int:
+ for region_id, region in enumerate(self.regions):
+ if area.fits_with(region):
+ return region_id
+ return None
+
+ def __memory_area_config_create(self, key):
+ param_dict = self.param_dict
+ area = param_dict[key][0]
+ area_name = param_dict[key][1]
+
+ region_id = self.__memory_area_find_region_id(area)
+ region = self.regions[region_id]
+ region_name = region.type
+
+ offset = area.addr - region.addr
+ size = area.sz
+
+ area_config = MemoryAreaConfig(area_name,\
+ region_name, \
+ offset, \
+ size)
+
+ self.mem_areas.append(area_config)
+
+ # Update max sectors
+ slot_sectors = int((offset % region.erase_sz +
+ size + region.erase_sz - 1) //
+ region.erase_sz)
+
+ self.max_sectors = max(self.max_sectors, slot_sectors)
+
+ def __memory_areas_create(self):
+ # Generate FA indexes
+ param_dict = self.param_dict
+ bootloader_area = self.boot_layout.bootloader_area
+ main_app = self.apps[0]
+ boot_area = main_app.boot_area
+ upgrade_area = main_app.upgrade_area
+ scratch_area = self.boot_layout.scratch_area
+
+ param_dict.update({'bootloader': [bootloader_area, 'FLASH_AREA_BOOTLOADER', 0]})
+ param_dict.update({'app_1_boot': [boot_area, 'FLASH_AREA_IMG_1_PRIMARY', 1]})
+ param_dict.update({'app_1_upgrade': [upgrade_area, 'FLASH_AREA_IMG_1_SECONDARY', 2]})
+
+ self.primary_slots.append('FLASH_AREA_IMG_1_PRIMARY')
+ self.secondary_slots.append('FLASH_AREA_IMG_1_SECONDARY')
+
+ # Generate scratch area index
+ if self.boot_layout.has_scratch_area:
+ param_dict.update({'scratch_area': [scratch_area, 'FLASH_AREA_IMAGE_SCRATCH', 3]})
+
+ # Generate multiple app area indexes
+ multi_app_area_idx = 4
+ if len(self.apps) > 1:
+ for app_id, app in enumerate(self.apps[1:], 1):
+ idx = multi_app_area_idx + (app_id-1) * 2
+ app_num = app_id + 1
+ key = f'app_{app_num}_boot'
+ fmt = f'FLASH_AREA_IMG_{app_num}_PRIMARY'
+ param_dict.update({key: [app.boot_area, fmt, idx]})
+
+ self.primary_slots.append(fmt)
+
+ key = f'app_{app_num}_upgrade'
+ fmt = f'FLASH_AREA_IMG_{app_num}_SECONDARY'
+ param_dict.update({key: [app.upgrade_area, fmt, idx+1]})
+
+ self.secondary_slots.append(fmt)
+
+ # Generate status area indexes
+ if self.boot_layout.has_status_area:
+ area = self.boot_layout.status_area
+ idx = multi_app_area_idx + (len(self.apps)-1) * 2
+ fmt = 'FLASH_AREA_IMAGE_SWAP_STATUS'
+ param_dict.update({'status_area': [area, fmt, idx]})
+
+ # Create areas
+ for key in param_dict:
+ self.__memory_area_config_create(key)
+
+ def __source_gen(self):
+ path = f'{self.output_folder}/{self.output_name}.c'
+ include = f'{self.output_name}.h'
+
+ with open(path, "w", encoding='UTF-8') as f_out:
+ f_out.write(f'#include "{include}"\n')
+ f_out.write(f'#include "flash_map_backend.h"\n\n')
+ f_out.write('struct flash_device flash_devices[] =\n')
+ f_out.write('{\n')
+ for region in self.regions:
+ f_out.write('\t{\n')
+ f_out.write(f'\t\t.address = {hex(region.addr)}U,\n')
+ f_out.write(f'\t\t.size = {hex(region.sz)}U,\n')
+ f_out.write(f'\t\t.erase_size = {hex(region.erase_sz)}U,\n')
+ f_out.write(f'\t\t.erase_val = {hex(region.erase_val)}U,\n')
+ f_out.write(f'\t\t.device_id = {str(region.type)},\n')
+ f_out.write('\t},\n')
+ f_out.write('};\n\n')
+
+ f_out.write(f'struct flash_area flash_areas[] =\n')
+ f_out.write('{\n')
+ for area in self.mem_areas:
+ f_out.writelines('\n'.join([
+ '\t{',
+ f"\t\t.fa_id = {area.fa_id},",
+ f"\t\t.fa_device_id = {area.fa_device_id},",
+ f"\t\t.fa_off = {hex(area.fa_off)}U,",
+ f"\t\t.fa_size = {hex(area.fa_size)}U,",
+ '\t},\n']))
+ f_out.write('};\n\n')
+
+ f_out.write('struct flash_area *boot_area_descs[] =\n')
+ f_out.write('{\n')
+ for index, area in enumerate(self.mem_areas):
+ f_out.write(f'\t&flash_areas[{index}U],\n')
+ f_out.write('\tNULL\n};\n\n')
+
+ f_out.write('uint8_t memory_areas_primary[] =\n')
+ f_out.write('{\n')
+ for slot in self.primary_slots:
+ f_out.write(f'\t{slot}, ')
+ f_out.write('\n};\n\n')
+
+ f_out.write('uint8_t memory_areas_secondary[] =\n')
+ f_out.write('{\n')
+ for slot in self.secondary_slots:
+ f_out.write(f'\t{slot}, ')
+ f_out.write('\n};\n\n')
+
+ def __header_gen(self):
+ path = f'{self.output_folder}/{self.output_name}.h'
+ with open(path, "w", encoding='UTF-8') as f_out:
+ header_guard_generate(f_out)
+
+ f_out.write(f'#include <stdint.h>\n')
+ f_out.write(f'#include "flash_map_backend.h"\n\n')
+ f_out.write(f'#define MEMORYMAP_GENERATED_AREAS 1\n\n')
+ f_out.write('extern struct flash_device flash_devices[];\n')
+ f_out.write('extern struct flash_area *boot_area_descs[];\n\n')
+ f_out.write('extern uint8_t memory_areas_primary[];\n')
+ f_out.write('extern uint8_t memory_areas_secondary[];\n\n')
+
+ f_out.write('enum \n{\n')
+ for id, type in enumerate(self.region_types):
+ f_out.write(f'\t{type} = {id}U,\n')
+ f_out.write('};\n\n')
+
+ f_out.write('enum \n{\n')
+ for area_param in self.param_dict.values():
+ f_out.write(f'\t{area_param[1]} = {area_param[2]}U,\n')
+ f_out.write('};\n\n')
+
+ def __bootloader_mk_file_gen(self):
+ boot = self.boot_layout
+ # Upgrade mode
+ if boot.scratch_area is None and boot.status_area is None:
+ print(settings_dict['overwrite'], ':= 1')
+ else:
+ print(settings_dict['overwrite'], ':= 0')
+ print(settings_dict['swap'], ':= 1')
+ print(settings_dict['scratch'], f':= {0 if boot.scratch_area is None else 1}')
+ print(settings_dict['status'], f':= {0 if boot.status_area is None else 1}')
+ print('# Shared data')
+ if boot.shared_data is not None:
+ shared_data = boot.shared_data
+ print(settings_dict['measured_boot'], ':= 1')
+ print(settings_dict['data_sharing'], ':= 1')
+
+ print(f'{settings_dict["shared_data_address"]} :=', hex(shared_data.addr))
+ print(f'{settings_dict["shared_data_size"]} :=', hex(shared_data.sz))
+ print(f'{settings_dict["shared_data_record_size"]} :=', hex(shared_data.sz))
+
+ print('# Bootloader app area')
+ print(f'{settings_dict["bootloader_app_address"]} :=', hex(boot.bootloader_area.addr))
+ print(f'{settings_dict["bootloader_app_size"]} :=', hex(boot.bootloader_area.sz))
+
+ print('# Bootloader ram area')
+ if boot.ram is not None:
+ print(f'{settings_dict["bootloader_ram_address"]} :=', hex(boot.ram.addr))
+ print(f'{settings_dict["bootloader_ram_size"]} :=', hex(boot.ram.sz))
+
+ print('# Application area')
+ for id, app in enumerate(self.apps):
+ print(f'APPLICATION_{id+1}_BOOT_SLOT_ADDRESS := {hex(app.boot_area.addr)}')
+ print(f'APPLICATION_{id+1}_BOOT_SLOT_SIZE := {hex(app.boot_area.sz)}')
+ print(f'APPLICATION_{id+1}_UPGRADE_SLOT_ADDRESS := {hex(app.upgrade_area.addr)}')
+ print(f'APPLICATION_{id+1}_UPGRADE_SLOT_SIZE := {hex(app.upgrade_area.sz)}')
+
+ print('# Ram load')
+ # Ram load single
+ if len(self.apps) == 1:
+ if self.apps[0].ram_boot is not None:
+ ram_boot = self.apps[0].ram_boot
+ print(settings_dict['ram_load'], ':= 1')
+ print(f'{settings_dict["ram_load_address"]} :=', hex(ram_boot.addr))
+ print(f'{settings_dict["ram_load_size"]} :=', hex(ram_boot.sz))
+ else:
+ # Ram load multiple
+ ram_boot_counter = 0
+ ram_addr_overlap_counter = 0
+
+ for app1 in self.apps:
+ for app2 in self.apps:
+ if app1 is app2:
+ continue
+ if app1.overlaps_with(app2):
+ ram_addr_overlap_counter += 1
+
+ for id, app in enumerate(self.apps):
+ if app.ram_boot is not None:
+ ram_boot_counter += 1
+ ram_boot = app.ram_boot
+
+ print(f'APPLICATION_{id+1}_RAM_LOAD_ADDRESS := {hex(ram_boot.addr)}')
+ print(f'APPLICATION_{id+1}_RAM_LOAD_SIZE := {hex(ram_boot.sz)}')
+
+ if ram_boot_counter != 0:
+ print(settings_dict['ram_load'], ':= 1')
+
+ if ram_boot_counter != len(self.apps) or ram_addr_overlap_counter == 0:
+ print(settings_dict['multi_memory_load'], ':= 1')
+
+ print('# Mcuboot')
+ print(settings_dict['application_count'], f'= {len(self.apps)}')
+ print(settings_dict['sectors_count'], f'= {self.max_sectors}')
+
+ def __application_mk_file_gen(self):
+ app = self.apps[self.app_id-1]
+ boot = self.boot_layout
+ # Upgrade mode
+ if boot.scratch_area is None and boot.status_area is None:
+ print(settings_dict['overwrite'], ':= 1')
+ else:
+ print(settings_dict['overwrite'], ':= 0')
+ print(settings_dict['swap'], ':= 1')
+ print(settings_dict['scratch'], f':= {0 if boot.scratch_area is None else 1}')
+ print(settings_dict['status'], f':= {0 if boot.status_area is None else 1}')
+
+ print(settings_dict['application_count'], f'= {len(self.apps)}')
+ print(settings_dict['boot_image'], ':=', self.app_id)
+ print(settings_dict['primary_image_start'], ':=', hex(app.boot_area.addr))
+ print(settings_dict['secondary_image_start'], ':=', hex(app.upgrade_area.addr))
+ print(settings_dict['image_size'], ':=', hex(app.boot_area.sz))
+ if app.ram_boot:
+ print(settings_dict['ram_load'], ':= 1')
+ if app.ram:
+ print(settings_dict['image_ram_address'], ':=', hex(app.ram.addr))
+ print(settings_dict['image_ram_size'], ':=', hex(app.ram.sz))
+ if app.core_name:
+ print(settings_dict['core'], ':=', app.core_name)
+
+ def parse(self, memory_map, platform_config, output_folder, output_name, app_id):
+ try:
+ with open(memory_map, "r", encoding='UTF-8') as f_in:
+ self.map_json = json.load(f_in)
+
+ with open(platform_config, "r", encoding='UTF-8') as f_in:
+ self.platform_json = json.load(f_in)
+
+ self.output_folder = output_folder
+ self.output_name = output_name
+
+ if app_id is not None:
+ self.app_id = int(app_id)
+
+ self.__memory_regions_init()
+ self.__boot_layout_init()
+ self.__apps_init()
+ self.__memory_areas_create()
+
+ self.__source_gen()
+ self.__header_gen()
+
+ if app_id is None:
+ self.__bootloader_mk_file_gen()
+ else:
+ self.__application_mk_file_gen()
+
+ except (FileNotFoundError, OSError):
+ print('\nERROR: Cannot open ', f_in, file=sys.stderr)
+ sys.exit(-1)
+
+
+@click.group()
+def cli():
+ '''
+ Memory map layout parser-configurator
+ '''
+
+@cli.command()
+@click.option('-i', '--memory_config', required=True,
+ help='memory configuration file path')
+@click.option('-p', '--platform_config', required=True,
+ help='platform configuration file path')
+@click.option('-n', '--output_name', required=True,
+ help='generated areas path')
+@click.option('-o', '--output_folder', required=True,
+ help='generated regions path')
+@click.option('-d', '--image_id', required=False,
+ help='application image number')
+
+def run(memory_config, platform_config, output_folder, output_name, image_id):
+ map = MemoryMap()
+ map.parse(memory_config,
+ platform_config,
+ output_folder,
+ output_name,
+ image_id)
+
+if __name__ == '__main__':
+ cli()
\ No newline at end of file