blob: 856e43064bf4bf72b56b942ed06de14f71fdced2 [file] [log] [blame]
Roman Okhrimenko977b3752022-03-31 14:40:48 +03001"""MCUBoot Flash Map Converter (JSON to .h)
2Copyright (c) 2022 Infineon Technologies AG
3"""
4
5import sys
6import getopt
7import json
8
9# Supported Platforms
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +000010cm0pCore = {
11 'cortex-m0+': 'CM0P',
12 'cm0+': 'CM0P',
13 'm0+': 'CM0P',
14 'cortex-m0p': 'CM0P',
15 'cm0p': 'CM0P',
16 'm0p': 'CM0P',
17 'cortex-m0plus': 'CM0P',
18 'cm0plus': 'CM0P',
19 'm0plus': 'CM0P'
20}
21
22cm4Core = {
23 'cortex-m4': 'CM4',
24 'cm4': 'CM4',
25 'm4': 'CM4'
26}
27
28cm33Core = {
29 'cortex-m33': 'CM33',
30 'cm33': 'CM33',
31 'm33': 'CM33'
32}
33
34allCores_PSOC_06x = {**cm0pCore, **cm4Core}
35
36common_PSOC_061 = {
37 'flashAddr': 0x10000000,
38 'eraseSize': 0x200, # 512 bytes
39 'smifAddr': 0x18000000,
40 'smifSize': 0x8000000, # i.e., window size
41 'VTAlign': 0x400, # Vector Table alignment
42 'allCores': cm4Core,
43 'bootCore': 'Cortex-M4',
44 'appCore': 'Cortex-M4'
45}
46
47common_PSOC_06x = {
48 'flashAddr': 0x10000000,
49 'eraseSize': 0x200, # 512 bytes
50 'smifAddr': 0x18000000,
51 'smifSize': 0x8000000, # i.e., window size
52 'VTAlign': 0x400, # Vector Table alignment
53 'allCores': allCores_PSOC_06x,
54 'bootCore': 'Cortex-M0+',
55 'appCore': 'Cortex-M4'
56}
57
Roman Okhrimenko977b3752022-03-31 14:40:48 +030058platDict = {
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +000059 'PSOC_061_2M': {
Roman Okhrimenko977b3752022-03-31 14:40:48 +030060 'flashSize': 0x200000, # 2 MBytes
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +000061 **common_PSOC_061
62 },
63 'PSOC_061_1M': {
64 'flashSize': 0x100000, # 1 MByte
65 **common_PSOC_061
66 },
67 'PSOC_061_512K': {
68 'flashSize': 0x80000, # 512 KBytes
69 **common_PSOC_061
70 },
71
72 'PSOC_062_2M': {
73 'flashSize': 0x200000, # 2 MBytes
74 **common_PSOC_06x
Roman Okhrimenko977b3752022-03-31 14:40:48 +030075 },
76 'PSOC_062_1M': {
Roman Okhrimenko977b3752022-03-31 14:40:48 +030077 'flashSize': 0x100000, # 1 MByte
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +000078 **common_PSOC_06x
Roman Okhrimenko977b3752022-03-31 14:40:48 +030079 },
80 'PSOC_062_512K': {
Roman Okhrimenko977b3752022-03-31 14:40:48 +030081 'flashSize': 0x80000, # 512 KBytes
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +000082 **common_PSOC_06x
Roman Okhrimenko977b3752022-03-31 14:40:48 +030083 },
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +000084
85 'PSOC_063_1M': {
86 'flashSize': 0x100000, # 1 MByte
87 **common_PSOC_06x
88 },
89
Roman Okhrimenko977b3752022-03-31 14:40:48 +030090 'CYW20829': {
91 'flashSize': 0, # n/a
92 'smifAddr': 0x60000000,
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +000093 'smifSize': 0x8000000, # i.e., window size
94 'VTAlign': 0x200, # Vector Table alignment
95 'allCores': cm33Core,
96 'bootCore': 'Cortex-M33',
97 'appCore': 'Cortex-M33',
98 'bitsPerCnt': False
99 },
100
101
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300102}
103
104# Supported SPI Flash ICs
105flashDict = {
106 # Fudan
107 'FM25Q04': {
108 'flashSize': 0x80000, # 4 Mbits
109 'eraseSize': 0x1000, # 128 uniform sectors with 4K-byte each
110 },
111 'FM25W04': {
112 'flashSize': 0x80000, # 4 Mbits
113 'eraseSize': 0x1000, # 128 uniform sectors with 4K-byte each
114 },
115 'FM25Q08': {
116 'flashSize': 0x100000, # 8 Mbits
117 'eraseSize': 0x1000, # 256 uniform sectors with 4K-byte each
118 },
119 'FM25W08': {
120 'flashSize': 0x100000, # 8 Mbits
121 'eraseSize': 0x1000, # 256 uniform sectors with 4K-byte each
122 },
123 # Puya
124 'P25Q05H': {
125 'flashSize': 0x10000, # 512 Kbits
126 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
127 },
128 'P25Q10H': {
129 'flashSize': 0x20000, # 1 Mbit
130 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
131 },
132 'P25Q20H': {
133 'flashSize': 0x40000, # 2 Mbits
134 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
135 },
136 'P25Q40H': {
137 'flashSize': 0x80000, # 4 Mbits
138 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
139 },
140 # Infineon
141 'S25HS256T': {
142 'flashSize': 0x2000000, # 256 Mbits
143 'eraseSize': 0x40000, # Uniform Sector Architecture
144 },
145 'S25HS512T': {
146 'flashSize': 0x4000000, # 512 Mbits
147 'eraseSize': 0x40000, # Uniform Sector Architecture
148 },
149 'S25HS01GT': {
150 'flashSize': 0x8000000, # 1 Gbit
151 'eraseSize': 0x40000, # Uniform Sector Architecture
152 }
153}
154
155
156def is_overlap(fa1off, fa1size, fa2off, fa2size, align):
157 """Check if two flash areas on the same device overlap"""
158 mask = align - 1
159 assert align > 0 and (align & mask) == 0 # ensure align is a power of 2
160 fa1end = (fa1off + fa1size + mask) & ~mask
161 fa2end = (fa2off + fa2size + mask) & ~mask
162 fa1off = fa1off & ~mask
163 fa2off = fa2off & ~mask
164 return fa1off < fa2end and fa2off < fa1end
165
166
167def is_same_mem(fa1addr, fa2addr):
168 """Check if two addresses belong to the same memory"""
169 if fa1addr is None or fa2addr is None:
170 return False
171 mask = 0xFF000000
172 return (fa1addr & mask) == (fa2addr & mask)
173
174
175class CmdLineParams:
176 """Command line parameters"""
177
178 def __init__(self):
179 self.plat_id = ''
180 self.in_file = ''
181 self.out_file = ''
182 self.img_id = None
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000183 self.policy = None
184 self.set_core = False
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300185
186 usage = 'USAGE:\n' + sys.argv[0] + \
187 ''' -p <platform> -i <flash_map.json> -o <flash_map.h> -d <img_id>
188
189OPTIONS:
190-h --help Display the usage information
191-p --platform= Target (e.g., PSOC_062_512K)
192-i --ifile= JSON flash map file
193-o --ofile= C header file to be generated
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000194-d --img_id ID of application to build
195-c --policy Policy file in JSON format
196-m --core Detect and set Cortex-M CORE
197'''
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300198
199 try:
200 opts, unused = getopt.getopt(
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000201 sys.argv[1:], 'hi:o:p:d:c:m',
202 ['help', 'platform=', 'ifile=', 'ofile=', 'img_id=', 'policy=', 'core'])
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300203 if len(unused) > 0:
204 print(usage, file=sys.stderr)
205 sys.exit(1)
206 except getopt.GetoptError:
207 print(usage, file=sys.stderr)
208 sys.exit(1)
209
210 for opt, arg in opts:
211 if opt in ('-h', '--help'):
212 print(usage, file=sys.stderr)
213 sys.exit()
214 elif opt in ('-p', '--platform'):
215 self.plat_id = arg
216 elif opt in ('-i', '--ifile'):
217 self.in_file = arg
218 elif opt in ('-o', '--ofile'):
219 self.out_file = arg
220 elif opt in ('-d', '--img_id'):
221 self.img_id = arg
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000222 elif opt in ('-c', '--policy'):
223 self.policy = arg
224 elif opt in ('-m', '--core'):
225 self.set_core = True
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300226
227 if len(self.in_file) == 0 or len(self.out_file) == 0:
228 print(usage, file=sys.stderr)
229 sys.exit(1)
230
231
232class AreaList:
233 """List of flash areas"""
234
235 def __init__(self, plat, flash, use_overwrite):
236 self.plat = plat
237 self.flash = flash
238 self.use_overwrite = use_overwrite
239 self.areas = []
240 self.peers = {}
241 self.trailers = {}
242 self.internal_flash = False
243 self.external_flash = False
244 self.external_flash_xip = False
245
246 def get_min_erase_size(self):
247 """Calculate minimum erase block size for int./ext. Flash """
248 return self.plat['eraseSize'] if self.plat['flashSize'] > 0 \
249 else self.flash['eraseSize']
250
251 def get_img_trailer_size(self):
252 """Calculate image trailer size"""
253 return self.get_min_erase_size()
254
255 def process_int_area(self, title, fa_addr, fa_size,
256 img_trailer_size, shared_slot):
257 """Process internal flash area"""
258 fa_device_id = 'FLASH_DEVICE_INTERNAL_FLASH'
259 fa_off = fa_addr - self.plat['flashAddr']
260 if img_trailer_size is not None:
261 if self.use_overwrite:
262 if shared_slot:
263 print('Shared slot', title,
264 'is not supported in OVERWRITE mode',
265 file=sys.stderr)
266 sys.exit(7)
267 else:
268 # Check trailer alignment (start at the sector boundary)
269 align = (fa_off + fa_size - img_trailer_size) % \
270 self.plat['eraseSize']
271 if align != 0:
272 fa_addr += self.plat['eraseSize'] - align
273 if fa_addr + fa_size <= \
274 self.plat['flashAddr'] + self.plat['flashSize']:
275 print('Misaligned', title,
276 '- suggested address', hex(fa_addr),
277 file=sys.stderr)
278 else:
279 print('Misaligned', title, file=sys.stderr)
280 sys.exit(7)
281 else:
282 # Check alignment (flash area should start at the sector boundary)
283 if fa_off % self.plat['eraseSize'] != 0:
284 print('Misaligned', title, file=sys.stderr)
285 sys.exit(7)
286 slot_sectors = int((fa_off % self.plat['eraseSize'] +
287 fa_size + self.plat['eraseSize'] - 1) //
288 self.plat['eraseSize'])
289 return fa_device_id, fa_off, slot_sectors
290
291 def process_ext_area(self, title, fa_addr, fa_size,
292 img_trailer_size, shared_slot):
293 """Process external flash area"""
294 if self.flash is None:
295 print('Unspecified SPI Flash IC',
296 file=sys.stderr)
297 sys.exit(3)
298 if fa_addr + fa_size <= \
299 self.plat['smifAddr'] + self.flash['flashSize']:
300 flash_idx = 'CY_BOOT_EXTERNAL_DEVICE_INDEX'
301 fa_device_id = f'FLASH_DEVICE_EXTERNAL_FLASH({flash_idx})'
302 fa_off = fa_addr - self.plat['smifAddr']
303 else:
304 print('Misfitting', title, file=sys.stderr)
305 sys.exit(7)
306 if img_trailer_size is not None:
307 if self.use_overwrite:
308 if shared_slot:
309 print('Shared slot', title,
310 'is not supported in OVERWRITE mode',
311 file=sys.stderr)
312 sys.exit(7)
313 else:
314 # Check trailer alignment (start at the sector boundary)
315 align = (fa_off + fa_size - img_trailer_size) % \
316 self.flash['eraseSize']
317 if align != 0:
318 peer_addr = self.peers.get(fa_addr)
319 if shared_slot:
320 # Special case when using both int. and ext. memory
321 if self.plat['flashSize'] > 0 and \
322 align % self.plat['eraseSize'] == 0:
323 print('Note:', title, 'requires', align,
324 'padding bytes before trailer',
325 file=sys.stderr)
326 else:
327 print('Misaligned', title, file=sys.stderr)
328 sys.exit(7)
329 elif is_same_mem(fa_addr, peer_addr) and \
330 fa_addr % self.flash['eraseSize'] == \
331 peer_addr % self.flash['eraseSize']:
332 pass # postpone checking
333 else:
334 fa_addr += self.flash['eraseSize'] - align
335 if fa_addr + fa_size <= \
336 self.plat['smifAddr'] + self.flash['flashSize']:
337 print('Misaligned', title,
338 '- suggested address', hex(fa_addr),
339 file=sys.stderr)
340 else:
341 print('Misaligned', title, file=sys.stderr)
342 sys.exit(7)
343 else:
344 # Check alignment (flash area should start at the sector boundary)
345 if fa_off % self.flash['eraseSize'] != 0:
346 print('Misaligned', title, file=sys.stderr)
347 sys.exit(7)
348 slot_sectors = int((fa_off % self.flash['eraseSize'] +
349 fa_size + self.flash['eraseSize'] - 1) //
350 self.flash['eraseSize'])
351 self.external_flash = True
352 if self.flash['XIP']:
353 self.external_flash_xip = True
354 return fa_device_id, fa_off, slot_sectors
355
356 def chk_area(self, fa_addr, fa_size, peer_addr=None):
357 """Check area location (internal/external flash)"""
358 if peer_addr is not None:
359 self.peers[peer_addr] = fa_addr
360 fa_limit = fa_addr + fa_size
361 if self.plat['flashSize'] and \
362 fa_addr >= self.plat['flashAddr'] and \
363 fa_limit <= self.plat['flashAddr'] + self.plat['flashSize']:
364 # Internal flash
365 self.internal_flash = True
366
367 def add_area(self, title,
368 fa_id, fa_addr, fa_size,
369 img_trailer_size=None, shared_slot=False):
370 """Add flash area to AreaList.
371 Internal/external flash is detected by address.
372 Returns number of sectors in a slot"""
373 if fa_size == 0:
374 print('Empty', title, file=sys.stderr)
375 sys.exit(7)
376
377 fa_limit = fa_addr + fa_size
378 if self.plat['flashSize'] and \
379 fa_addr >= self.plat['flashAddr'] and \
380 fa_limit <= self.plat['flashAddr'] + self.plat['flashSize']:
381 # Internal flash
382 fa_device_id, fa_off, slot_sectors = self.process_int_area(
383 title, fa_addr, fa_size, img_trailer_size, shared_slot)
384 align = self.plat['eraseSize']
385 elif self.plat['smifSize'] and \
386 fa_addr >= self.plat['smifAddr'] and \
387 fa_limit <= self.plat['smifAddr'] + self.plat['smifSize']:
388 # External flash
389 fa_device_id, fa_off, slot_sectors = self.process_ext_area(
390 title, fa_addr, fa_size, img_trailer_size, shared_slot)
391 align = self.flash['eraseSize']
392 else:
393 print('Invalid', title, file=sys.stderr)
394 sys.exit(7)
395
396 if shared_slot:
397 assert img_trailer_size is not None
398 tr_addr = fa_addr + fa_size - img_trailer_size
399 tr_name = self.trailers.get(tr_addr)
400 if tr_name is not None:
401 print('Same trailer address for', title, 'and', tr_name,
402 file=sys.stderr)
403 sys.exit(7)
404 self.trailers[tr_addr] = title
405
406 # Ensure no flash areas on this device will overlap, except the
407 # shared slot
408 for area in self.areas:
409 if fa_device_id == area['fa_device_id']:
410 over = is_overlap(fa_off, fa_size,
411 area['fa_off'], area['fa_size'],
412 align)
413 if shared_slot and area['shared_slot']:
414 if not over: # images in shared slot should overlap
415 print(title, 'is not shared with', area['title'],
416 file=sys.stderr)
417 sys.exit(7)
418 elif over:
419 print(title, 'overlaps with', area['title'],
420 file=sys.stderr)
421 sys.exit(7)
422
423 self.areas.append({'title': title,
424 'shared_slot': shared_slot,
425 'fa_id': fa_id,
426 'fa_device_id': fa_device_id,
427 'fa_off': fa_off,
428 'fa_size': fa_size})
429 return slot_sectors
430
431 def generate_c_source(self, params):
432 """Generate C source"""
433 c_array = 'flash_areas'
434
435 try:
436 with open(params.out_file, "w", encoding='UTF-8') as out_f:
437 out_f.write('/* AUTO-GENERATED FILE, DO NOT EDIT.'
Roman Okhrimenko25165622023-05-23 08:58:29 +0300438 ' ALL CHANGES WILL BE LOST! */\n')
439 out_f.write(f'/* Platform: {params.plat_id} */\n')
440 out_f.write("#ifndef CY_FLASH_MAP_H\n")
441 out_f.write("#define CY_FLASH_MAP_H\n\n")
442
443 if self.plat.get('bitsPerCnt'):
444 out_f.write('#ifdef NEED_FLASH_MAP\n')
445 out_f.write(f'static struct flash_area {c_array}[] = {{\n')
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300446 comma = len(self.areas)
447 area_count = 0
448 for area in self.areas:
449 comma -= 1
450 if area['fa_id'] is not None:
451 sss = ' /* Shared secondary slot */' \
452 if area['shared_slot'] else ''
453 out_f.writelines('\n'.join([
454 ' {' + sss,
455 f" .fa_id = {area['fa_id']},",
456 f" .fa_device_id = {area['fa_device_id']},",
457 f" .fa_off = {hex(area['fa_off'])}U,",
458 f" .fa_size = {hex(area['fa_size'])}U",
459 ' },' if comma else ' }', '']))
460 area_count += 1
461 out_f.write('};\n\n'
462 'struct flash_area *boot_area_descs[] = {\n')
463 for area_index in range(area_count):
464 out_f.write(f' &{c_array}[{area_index}U],\n')
Roman Okhrimenko25165622023-05-23 08:58:29 +0300465 out_f.write(' NULL\n};\n')
466
467 if self.plat.get('bitsPerCnt'):
468 out_f.write('#endif /* NEED_FLASH_MAP */\n')
469 out_f.close()
470
471 # inserted here to fix misra 'header guard'
472 list_counters = process_policy_20829(params.policy)
473 if list_counters is not None:
474 form_max_counter_array(list_counters, params.out_file)
475 with open(params.out_file, "a", encoding='UTF-8') as out_f:
476 out_f.write("#endif /* CY_FLASH_MAP_H */\n")
477 else:
478 out_f.write("#endif /* CY_FLASH_MAP_H */\n")
479
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300480 except (FileNotFoundError, OSError):
481 print('Cannot create', params.out_file, file=sys.stderr)
482 sys.exit(4)
483
484
485def cvt_dec_or_hex(val, desc):
486 """Convert (hexa)decimal string to number"""
487 try:
488 return int(val, 0)
489 except ValueError:
490 print('Invalid value', val, 'for', desc, file=sys.stderr)
491 sys.exit(6)
492
493
494def get_val(obj, attr):
495 """Get JSON 'value'"""
496 obj = obj[attr]
497 try:
498 return cvt_dec_or_hex(obj['value'], obj['description'])
499 except KeyError as key:
500 print('Malformed JSON:', key,
501 'is missing in', "'" + attr + "'",
502 file=sys.stderr)
503 sys.exit(5)
504
505
506def get_bool(obj, attr, def_val=False):
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000507 """Get JSON boolean value (returns def_val if it is missing)"""
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300508 ret_val = def_val
509 obj = obj.get(attr)
510 if obj is not None:
511 try:
512 val = str(obj['value']).lower()
513 desc = obj['description']
514 if val == 'true':
515 ret_val = True
516 elif val == 'false':
517 ret_val = False
518 else:
519 print('Invalid value', val, 'for', desc, file=sys.stderr)
520 sys.exit(6)
521 except KeyError as key:
522 print('Malformed JSON:', key,
523 'is missing in', "'" + attr + "'",
524 file=sys.stderr)
525 sys.exit(5)
526 return ret_val
527
528
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000529def get_str(obj, attr, def_val=None):
530 """Get JSON string value (returns def_val if it is missing)"""
531 ret_val = def_val
532 obj = obj.get(attr)
533 if obj is not None:
534 try:
535 ret_val = str(obj['value'])
536 except KeyError as key:
537 print('Malformed JSON:', key,
538 'is missing in', "'" + attr + "'",
539 file=sys.stderr)
540 sys.exit(5)
541 return ret_val
542
543
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300544class AddrSize:
545 """Bootloader area"""
546
547 def __init__(self, bootloader, addr_name, size_name):
548 self.fa_addr = get_val(bootloader, addr_name)
549 self.fa_size = get_val(bootloader, size_name)
550
551
552def calc_status_size(boot_swap_status_row_sz, max_img_sectors,
553 img_number, scratch_flag=True):
554 """Estimate status size, see swap_status.h"""
555 boot_swap_status_cnt_sz = 4
556 boot_swap_status_crc_sz = 4
557 boot_swap_status_mgcrec_sz = 4
558 boot_swap_status_trailer_size = 64
559 boot_swap_status_payld_sz = \
560 boot_swap_status_row_sz - boot_swap_status_mgcrec_sz - \
561 boot_swap_status_cnt_sz - boot_swap_status_crc_sz
562 boot_swap_status_sect_rows_num = \
563 int((max_img_sectors - 1) //
564 boot_swap_status_payld_sz) + 1
565 boot_swap_status_trail_rows_num = \
566 int((boot_swap_status_trailer_size - 1) //
567 boot_swap_status_payld_sz) + 1
568 boot_swap_status_d_size = \
569 boot_swap_status_row_sz * \
570 (boot_swap_status_sect_rows_num + boot_swap_status_trail_rows_num)
571 boot_swap_status_mult = 2
572 boot_swap_status_size = boot_swap_status_mult * boot_swap_status_d_size
573 status_zone_cnt = 2 * img_number
574 if scratch_flag:
575 status_zone_cnt += 1
576 return boot_swap_status_size * status_zone_cnt
577
578
579def process_json(in_file):
580 """Process JSON"""
581 try:
582 with open(in_file, encoding='UTF-8') as in_f:
583 try:
584 flash_map = json.load(in_f)
585 except ValueError:
586 print('Cannot parse', in_file, file=sys.stderr)
587 sys.exit(4)
588 except (FileNotFoundError, OSError):
589 print('Cannot open', in_file, file=sys.stderr)
590 sys.exit(4)
591 flash = flash_map.get('external_flash')
592 if flash is not None:
593 flash = flash[0]
594 model = flash.get('model')
595 mode = flash.get('mode')
596 if model is not None:
597 try:
598 flash = flashDict[model]
599 except KeyError:
600 print('Supported SPI Flash ICs are:',
601 ', '.join(flashDict.keys()),
602 file=sys.stderr)
603 sys.exit(3)
604 else:
605 try:
606 flash = {'flashSize': cvt_dec_or_hex(flash['flash-size'],
607 'flash-size'),
608 'eraseSize': cvt_dec_or_hex(flash['erase-size'],
609 'erase-size')}
610 except KeyError as key:
611 print('Malformed JSON:', key,
612 "is missing in 'external_flash'",
613 file=sys.stderr)
614 sys.exit(3)
615 flash.update({'XIP': str(mode).upper() == 'XIP'})
616 return flash_map['boot_and_upgrade'], flash
617
618
619def process_images(area_list, boot_and_upgrade):
620 """Process images"""
621 app_count = 0
622 slot_sectors_max = 0
623 all_shared = get_bool(boot_and_upgrade['bootloader'], 'shared_slot')
624 any_shared = all_shared
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000625 app_core = None
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300626 apps_flash_map = [None, ]
627
628 for stage in range(2):
629 for app_index in range(1, 5):
630
631 app_flash_map = {}
632
633 try:
634 app_ident = f'application_{app_index}'
635 application = boot_and_upgrade[app_ident]
636 try:
637 primary_addr = get_val(application, 'address')
638 primary_size = get_val(application, 'size')
639 secondary_addr = get_val(application, 'upgrade_address')
640 secondary_size = get_val(application, 'upgrade_size')
641 except KeyError as key:
642 print('Malformed JSON:', key, 'is missing',
643 file=sys.stderr)
644 sys.exit(5)
645 if stage == 0:
646 if primary_size != secondary_size:
647 print('Primary and secondary slot sizes'
648 ' are different for', app_ident,
649 file=sys.stderr)
650 sys.exit(6)
651 area_list.chk_area(primary_addr, primary_size)
652 area_list.chk_area(secondary_addr, secondary_size,
653 primary_addr)
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000654 if application.get('core') is None:
655 if app_index == 1:
656 app_core = area_list.plat['appCore']
657 elif app_index > 1:
658 print('"core" makes sense only for the 1st app',
659 file=sys.stderr)
660 sys.exit(6)
661 else:
662 app_core = get_str(application, 'core',
663 area_list.plat['appCore'])
664 if app_index == 1:
665 app_core = area_list.plat['allCores'].get(app_core.lower())
666 if app_core is None:
667 print('Unknown "core"', file=sys.stderr)
668 sys.exit(6)
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300669 else:
670 slot_sectors_max = max(
671 slot_sectors_max,
672 area_list.add_area(
673 f'{app_ident} (primary slot)',
674 f'FLASH_AREA_IMG_{app_index}_PRIMARY',
675 primary_addr, primary_size,
676 area_list.get_img_trailer_size()))
677 shared_slot = get_bool(application, 'shared_slot', all_shared)
678 any_shared = any_shared or shared_slot
679 slot_sectors_max = max(
680 slot_sectors_max,
681 area_list.add_area(
682 f'{app_ident} (secondary slot)',
683 f'FLASH_AREA_IMG_{app_index}_SECONDARY',
684 secondary_addr, secondary_size,
685 area_list.get_img_trailer_size(),
686 shared_slot))
687
688 app_slot_prim = {"address": hex(primary_addr), "size": hex(primary_size)}
689 app_slot_sec = {"address": hex(secondary_addr), "size": hex(secondary_size)}
690
691 app_flash_map.update({"primary": app_slot_prim, "secondary": app_slot_sec})
692 apps_flash_map.append(app_flash_map)
693
694 app_count = app_index
695
696 except KeyError:
697 break
698 if app_count == 0:
699 print('Malformed JSON: no application(s) found',
700 file=sys.stderr)
701 sys.exit(5)
702
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000703 return app_core, app_count, slot_sectors_max, apps_flash_map, any_shared
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300704
Roman Okhrimenko25165622023-05-23 08:58:29 +0300705def process_policy_20829(in_policy):
706 """Process policy file to get data of NV-counter"""
707 list_counters = None
708
709 try:
710 with open(in_policy, encoding='UTF-8') as in_f:
711 try:
712 policy = json.load(in_f)
713 except ValueError:
714 print('\nERROR: Cannot parse', in_policy,'\n', file=sys.stderr)
715 sys.exit(4)
716 finally:
717 in_f.close()
718 except (FileNotFoundError, OSError):
719 print('Cannot open', in_policy, file=sys.stderr)
720 sys.exit(4)
721
722 try:
723 nv_cnt = policy["device_policy"]['reprovisioning']['nv_counter']
724 list_values = nv_cnt["value"]
725 list_counters = nv_cnt["bits_per_cnt"]
726 except KeyError:
727 print("\nERROR: Check path to 'nv_counter' and its correctness in policy file", in_policy,
728 ".\n", file=sys.stderr)
729 sys.exit(2)
730
731 #Check correctness of NV-counter
732 try:
733 len_list_value = len(list_values)
734 len_list_counters = len(list_counters)
735 except TypeError:
736 print("\nERROR: Fields 'value' and 'bits_per_cnt' of 'nv_counter' in policy file",
737 in_policy,"must be arrays.\n", file=sys.stderr)
738 sys.exit(2)
739
740 if len_list_value != len_list_counters:
741 print("\nERROR: Fields 'value' and 'bits_per_cnt' of 'nv_counter' in policy file",
742 in_policy,"must have the same size.\n", file=sys.stderr)
743 sys.exit(2)
744
745 sum_all_counters = 0
746 for i in range(len_list_value):
747 sum_all_counters += list_counters[i]
748 if list_values[i] > list_counters[i]:
749 print("\nERROR: Field 'value' cannot be more then 'bits_per_cnt'.", file=sys.stderr)
750 print("Check 'nv_counter' in policy file", in_policy,"\n", file=sys.stderr)
751 sys.exit(2)
752
753 sum_all_bit_nv_counter = 32
754 if sum_all_counters != sum_all_bit_nv_counter:
755 print("\nERROR: The sum of all 'bits_per_cnt' must be equal to 32.", file=sys.stderr)
756 print("Check 'nv_counter' in policy file", in_policy,"\n", file=sys.stderr)
757 sys.exit(2)
758
759 return list_counters
760
761
762def form_max_counter_array(in_list, out_file):
763 '''Write bit_per_count array to output file
764 There is expected, that "out_file" already exists'''
765
766 out_array_str = "\n#ifdef NEED_MAX_COUNTERS\nstatic const uint8_t bits_per_cnt[] = {"
767
768 #in_list is checked in prior function 'process_policy()'
769 for i, list_member in enumerate(in_list):
770 out_array_str += str(list_member)
771 if i < len(in_list) - 1:
772 out_array_str += ", "
773 out_array_str += "};\n#endif\n"
774
775 try:
776 with open(out_file, "a", encoding='UTF-8') as out_f:
777 out_f.write(out_array_str)
778 except (FileNotFoundError, OSError):
779 print('\nERROR: Cannot open ', out_file, file=sys.stderr)
780 sys.exit(7)
781
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300782
783def main():
784 """Flash map converter"""
785 params = CmdLineParams()
786
787 try:
788 plat = platDict[params.plat_id]
789 except KeyError:
790 print('Supported platforms are:', ', '.join(platDict.keys()),
791 file=sys.stderr)
792 sys.exit(2)
793
794 try:
795 boot_and_upgrade, flash = process_json(params.in_file)
796 bootloader = boot_and_upgrade['bootloader']
797 boot = AddrSize(bootloader, 'address', 'size')
798 except KeyError as key:
799 print('Malformed JSON:', key, 'is missing',
800 file=sys.stderr)
801 sys.exit(5)
802
803 try:
804 scratch = AddrSize(bootloader, 'scratch_address', 'scratch_size')
805 except KeyError:
806 scratch = None
807
808 try:
809 swap_status = AddrSize(bootloader, 'status_address', 'status_size')
810 except KeyError:
811 swap_status = None
812
813 # Create flash areas
814 area_list = AreaList(plat, flash, scratch is None and swap_status is None)
815 area_list.add_area('bootloader', 'FLASH_AREA_BOOTLOADER',
816 boot.fa_addr, boot.fa_size)
817
818 # Service RAM app (optional)
819 service_app = boot_and_upgrade.get('service_app')
820 app_binary = None
821 input_params = None
822 app_desc = None
823 if service_app is not None:
824 if plat['flashSize'] > 0:
825 print('service_app is unsupported on this platform',
826 file=sys.stderr)
827 sys.exit(7)
828 try:
829 app_binary = AddrSize(service_app, 'address', 'size')
830 input_params = AddrSize(service_app, 'params_address', 'params_size')
831 app_desc = AddrSize(service_app, 'desc_address', 'desc_size')
832 if input_params.fa_addr != app_binary.fa_addr + app_binary.fa_size or \
833 app_desc.fa_addr != input_params.fa_addr + input_params.fa_size or \
834 app_desc.fa_size != 0x20:
835 print('Malformed service_app definition', file=sys.stderr)
836 sys.exit(7)
837 area_list.add_area('service_app', None, app_binary.fa_addr,
838 app_binary.fa_size + input_params.fa_size + app_desc.fa_size)
839 except KeyError as key:
840 print('Malformed JSON:', key, 'is missing',
841 file=sys.stderr)
842 sys.exit(5)
843
844 # Fill flash areas
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000845 app_core, app_count, slot_sectors_max, apps_flash_map, shared_slot = \
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300846 process_images(area_list, boot_and_upgrade)
847
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000848 cy_img_hdr_size = 0x400
849 app_start = int(apps_flash_map[1].get("primary").get("address"), 0) + cy_img_hdr_size
850
851 if app_start % plat['VTAlign'] != 0:
852 print('Starting address', apps_flash_map[1].get("primary").get("address"),
853 '+', hex(cy_img_hdr_size),
854 'must be aligned to', hex(plat['VTAlign']),
855 file=sys.stderr)
856 sys.exit(7)
857
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300858 slot_sectors_max = max(slot_sectors_max, 32)
859
860 if swap_status is not None:
861 status_size_min = calc_status_size(area_list.get_min_erase_size(),
862 slot_sectors_max,
863 app_count,
864 scratch is not None)
865
866 if swap_status.fa_size < status_size_min:
867 print('Insufficient swap status area - suggested size',
868 hex(status_size_min),
869 file=sys.stderr)
870 sys.exit(7)
871 area_list.add_area('swap status partition',
872 'FLASH_AREA_IMAGE_SWAP_STATUS',
873 swap_status.fa_addr, swap_status.fa_size)
874
875 if scratch is not None:
876 area_list.add_area('scratch area',
877 'FLASH_AREA_IMAGE_SCRATCH',
878 scratch.fa_addr, scratch.fa_size)
879
Roman Okhrimenko25165622023-05-23 08:58:29 +0300880 # Compare size 'bit_per_cnt' and number of images.
881 # 'service_app' is used only when HW rollback counter exists
882 if plat.get('bitsPerCnt') is not None and service_app is not None:
883 plat['bitsPerCnt'] = True
884 list_counters = process_policy_20829(params.policy)
885 if list_counters is not None and len(list_counters) != app_count:
886 print("\nERROR: 'bits_per_cnt' must be present for each image!",
887 file=sys.stderr)
888 print("Please, check secure provisioning and reprovisioning policies.\n",
889 file=sys.stderr)
890 sys.exit(7)
891
892
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300893 # Image id parameter is not used for MCUBootApp
894 if params.img_id is None:
895 area_list.generate_c_source(params)
896
897 # Report necessary values back to make
898 print('# AUTO-GENERATED FILE, DO NOT EDIT. ALL CHANGES WILL BE LOST!')
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000899 print('BOOTLOADER_SIZE :=', hex(boot.fa_size))
900 if params.set_core:
901 print('CORE :=', plat['allCores'][plat['bootCore'].lower()])
902 print('APP_CORE :=', app_core)
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300903
904 if params.img_id is not None:
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000905 primary_img_start = apps_flash_map[int(params.img_id)].get("primary").get("address")
906 secondary_img_start = apps_flash_map[int(params.img_id)].get("secondary").get("address")
907 slot_size = apps_flash_map[int(params.img_id)].get("primary").get("size")
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300908
909 print('PRIMARY_IMG_START := ' + primary_img_start)
910 print('SECONDARY_IMG_START := ' + secondary_img_start)
911 print('SLOT_SIZE := ' + slot_size)
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300912 else:
913 print('MCUBOOT_IMAGE_NUMBER :=', app_count)
914 print('MAX_IMG_SECTORS :=', slot_sectors_max)
915
916 if area_list.use_overwrite:
917 print('USE_OVERWRITE := 1')
918 if area_list.external_flash:
919 print('USE_EXTERNAL_FLASH := 1')
920 if area_list.external_flash_xip:
921 print('USE_XIP := 1')
922 if shared_slot:
923 print('USE_SHARED_SLOT := 1')
924 if service_app is not None:
925 print('PLATFORM_SERVICE_APP_OFFSET :=',
926 hex(app_binary.fa_addr - plat['smifAddr']))
927 print('PLATFORM_SERVICE_APP_INPUT_PARAMS_OFFSET :=',
928 hex(input_params.fa_addr - plat['smifAddr']))
929 print('PLATFORM_SERVICE_APP_DESC_OFFSET :=',
930 hex(app_desc.fa_addr - plat['smifAddr']))
931 print('USE_HW_ROLLBACK_PROT := 1')
932
933
934if __name__ == '__main__':
935 main()