blob: d0799d6a4a928b7d7fce8308a29c11348699c7c9 [file] [log] [blame]
"""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
MAX_IMAGE_NUMBERS = 16
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_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
}
common_XMC7000 = {
'flashAddr': 0x10000000,
'eraseSize': 0x8000, # 32k
'smifAddr': 0x18000000,
'smifSize': 0x8000000, # i.e., window size
'VTAlign': 0x400, # Vector Table alignment
'allCores': cm7Core,
'bootCore': 'Cortex-M7',
'appCore': 'Cortex-M7'
}
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': {
**common_CYW20829
},
'CYW89829': {
**common_CYW20829
}
}
# 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)
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, boot_and_upgrade):
'''
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')
image_boot_mode = None
if params.image_boot_config:
image_boot_mode = process_boot_type(boot_and_upgrade)
if image_boot_mode:
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')
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("#pragma once\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('#include "bootutil/bootutil.h"\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'):
list_counters = process_policy_20829(params.policy)
if list_counters is not None:
form_max_counter_array(list_counters, fa_f)
fa_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];'
]))
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:
mem = application.get('ram_boot')
if mem:
image_boot_mode.append(
{
'mode': 'IMAGE_BOOT_MODE_RAM',
'address': mem.get('address', {}).get('value', 0),
'size': mem.get('size', {}).get('value', 0),
}
)
else :
mem = application.get('flash')
image_boot_mode.append(
{
'mode': 'IMAGE_BOOT_MODE_FLASH',
'address': mem.get('address', {}).get('value', 0),
'size': mem.get('size', {}).get('value', 0),
}
)
return image_boot_mode
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, MAX_IMAGE_NUMBERS):
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, fa_f):
'''Write bit_per_count array to output file '''
#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"
fa_f.write(out_array_str)
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)
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, boot_and_upgrade)
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)
# for blinky
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)
# for bootloader
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:
print('USE_MCUBOOT_RAM_LOAD := 1')
if ram_load_counter == 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()