blob: 3215dc41c152e8cf2c699f3dc0a36be44852b39e [file] [log] [blame]
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001"""MCUBoot Flash Map Converter (JSON to .h)
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03002Copyright (c) 2025 Infineon Technologies AG
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03003"""
4
5import sys
6import getopt
7import json
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03008import os
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03009from enum import Enum
INFINEON\DovhalA21babb72025-07-18 10:36:03 +030010from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030011
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020012MAX_IMAGE_NUMBERS = 16
INFINEON\DovhalA21babb72025-07-18 10:36:03 +030013NONCE_GEN_RETRIES = 10
14NONCE_RAND_SIZE = 12
15AES_BLOCK_SIZE = 16
16IMG_OK_OFFSET = 8 + 16 # flag_size + magic_size
17IMG_OK_VALUE = 0x01
18SHAB_ADDR = 0x08000000
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020019
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030020class Error(Enum):
21 ''' Application error codes '''
22 ARG = 1
23 POLICY = 2
24 FLASH = 3
25 IO = 4
26 JSON = 5
27 VALUE = 6
28 CONFIG_MISMATCH = 7
29
30SERVICE_APP_SZ = 0x20
31
32c_array = 'flash_areas'
33
34# Supported Platforms
35cm0pCore = {
36 'cortex-m0+': 'CM0P',
37 'cm0+': 'CM0P',
38 'm0+': 'CM0P',
39 'cortex-m0p': 'CM0P',
40 'cm0p': 'CM0P',
41 'm0p': 'CM0P',
42 'cortex-m0plus': 'CM0P',
43 'cm0plus': 'CM0P',
44 'm0plus': 'CM0P'
45}
46
47cm4Core = {
48 'cortex-m4': 'CM4',
49 'cm4': 'CM4',
50 'm4': 'CM4'
51}
52
53cm33Core = {
54 'cortex-m33': 'CM33',
55 'cm33': 'CM33',
56 'm33': 'CM33'
57}
58
59cm7Core = {
60 'cortex-m7': 'CM7',
61 'cm7': 'CM7',
62 'm7': 'CM7'
63}
64
65allCores_PSOC_06x = {**cm0pCore, **cm4Core}
66
67common_PSOC_061 = {
68 'flashAddr': 0x10000000,
69 'eraseSize': 0x200, # 512 bytes
70 'smifAddr': 0x18000000,
71 'smifSize': 0x8000000, # i.e., window size
72 'VTAlign': 0x400, # Vector Table alignment
73 'allCores': cm4Core,
74 'bootCore': 'Cortex-M4',
75 'appCore': 'Cortex-M4'
76}
77
78common_PSOC_06x = {
79 'flashAddr': 0x10000000,
80 'eraseSize': 0x200, # 512 bytes
81 'smifAddr': 0x18000000,
82 'smifSize': 0x8000000, # i.e., window size
83 'VTAlign': 0x400, # Vector Table alignment
84 'allCores': allCores_PSOC_06x,
85 'bootCore': 'Cortex-M0+',
86 'appCore': 'Cortex-M4'
87}
88
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020089common_CYW20829 = {
90 'flashSize': 0, # n/a
91 'smifAddr': 0x60000000,
92 'smifSize': 0x8000000, # i.e., window size
93 'VTAlign': 0x200, # Vector Table alignment
94 'allCores': cm33Core,
95 'bootCore': 'Cortex-M33',
96 'appCore': 'Cortex-M33',
97 'bitsPerCnt': False
98}
99
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300100common_XMC7000 = {
101 'flashAddr': 0x10000000,
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200102 'eraseSize': 0x8000, # 32k
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300103 'smifAddr': 0x18000000,
104 'smifSize': 0x8000000, # i.e., window size
105 'VTAlign': 0x400, # Vector Table alignment
106 'allCores': cm7Core,
107 'bootCore': 'Cortex-M7',
108 'appCore': 'Cortex-M7'
109}
110
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300111platDict = {
112 'PSOC_061_2M': {
113 'flashSize': 0x200000, # 2 MBytes
114 **common_PSOC_061
115 },
116 'PSOC_061_1M': {
117 'flashSize': 0x100000, # 1 MByte
118 **common_PSOC_061
119 },
120 'PSOC_061_512K': {
121 'flashSize': 0x80000, # 512 KBytes
122 **common_PSOC_061
123 },
124
125 'PSOC_062_2M': {
126 'flashSize': 0x200000, # 2 MBytes
127 **common_PSOC_06x
128 },
129 'PSOC_062_1M': {
130 'flashSize': 0x100000, # 1 MByte
131 **common_PSOC_06x
132 },
133 'PSOC_062_512K': {
134 'flashSize': 0x80000, # 512 KBytes
135 **common_PSOC_06x
136 },
137
138 'PSOC_063_1M': {
139 'flashSize': 0x100000, # 1 MByte
140 **common_PSOC_06x
141 },
142
143 'XMC7200': {
144 'flashSize': 0x100000, # 1 MByte
145 **common_XMC7000
146 },
147
148 'XMC7100': {
149 'flashSize': 0x100000, # 1 MByte
150 **common_PSOC_06x
151 },
152
153 'CYW20829': {
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200154 **common_CYW20829
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300155 },
156
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200157 'CYW89829': {
158 **common_CYW20829
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300159 }
160}
161
162# Supported SPI Flash ICs
163flashDict = {
164 # Fudan
165 'FM25Q04': {
166 'flashSize': 0x80000, # 4 Mbits
167 'eraseSize': 0x1000, # 128 uniform sectors with 4K-byte each
168 },
169 'FM25W04': {
170 'flashSize': 0x80000, # 4 Mbits
171 'eraseSize': 0x1000, # 128 uniform sectors with 4K-byte each
172 },
173 'FM25Q08': {
174 'flashSize': 0x100000, # 8 Mbits
175 'eraseSize': 0x1000, # 256 uniform sectors with 4K-byte each
176 },
177 'FM25W08': {
178 'flashSize': 0x100000, # 8 Mbits
179 'eraseSize': 0x1000, # 256 uniform sectors with 4K-byte each
180 },
181 # Puya
182 'P25Q05H': {
183 'flashSize': 0x10000, # 512 Kbits
184 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
185 },
186 'P25Q10H': {
187 'flashSize': 0x20000, # 1 Mbit
188 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
189 },
190 'P25Q20H': {
191 'flashSize': 0x40000, # 2 Mbits
192 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
193 },
194 'P25Q40H': {
195 'flashSize': 0x80000, # 4 Mbits
196 'eraseSize': 0x1000, # Uniform 4K-byte Sector Erase
197 },
198 # Infineon
199 'S25HS256T': {
200 'flashSize': 0x2000000, # 256 Mbits
201 'eraseSize': 0x40000, # Uniform Sector Architecture
202 },
203 'S25HS512T': {
204 'flashSize': 0x4000000, # 512 Mbits
205 'eraseSize': 0x40000, # Uniform Sector Architecture
206 },
207 'S25HS01GT': {
208 'flashSize': 0x8000000, # 1 Gbit
209 'eraseSize': 0x40000, # Uniform Sector Architecture
210 }
211}
212
213
214def is_overlap(fa1off, fa1size, fa2off, fa2size, align):
215 """Check if two flash areas on the same device overlap"""
216 mask = align - 1
217 assert align > 0 and (align & mask) == 0 # ensure align is a power of 2
218 fa1end = (fa1off + fa1size + mask) & ~mask
219 fa2end = (fa2off + fa2size + mask) & ~mask
220 fa1off = fa1off & ~mask
221 fa2off = fa2off & ~mask
222 return fa1off < fa2end and fa2off < fa1end
223
224
225def is_same_mem(fa1addr, fa2addr):
226 """Check if two addresses belong to the same memory"""
227 if fa1addr is None or fa2addr is None:
228 return False
229 mask = 0xFF000000
230 return (fa1addr & mask) == (fa2addr & mask)
231
232
233class CmdLineParams:
234 """Command line parameters"""
235
236 def __init__(self):
237 self.plat_id = ''
238 self.in_file = ''
239 self.out_file = ''
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300240 self.mk_file = ''
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300241 self.fa_file = ''
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300242 self.nonce_file = None
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300243 self.img_id = None
244 self.policy = None
245 self.set_core = False
246 self.image_boot_config = False
247
248 usage = 'USAGE:\n' + sys.argv[0] + \
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300249 ''' -p <platform> -i <flash_map.json> -o <memorymap.c> -k <memorymap.mk> -a <memorymap.h> -d <img_id> -c <policy.json>
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300250
251OPTIONS:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300252-h --help Display the usage information
253-p --platform Target (e.g., PSOC_062_512K)
254-i --ifile JSON flash map file
255-o --ofile C file to be generated
256-k --mk_file Path where to create 'memorymap.mk'
257-a --fa_file Path where to create 'memorymap.h'
258-n --nonce_file Path where to create file with NONCE
259-d --img_id ID of application to build
260-c --policy Policy file in JSON format
261-m --core Detect and set Cortex-M CORE
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300262-x --image_boot_config Generate image boot config structure
263'''
264
265 try:
266 opts, unused = getopt.getopt(
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300267 sys.argv[1:], 'h:i:o:k:a:n:p:d:c:x:m',
268 ['help', 'platform=', 'ifile=', 'ofile=', 'mk_file=', 'fa_file=',
269 'nonce_file=', 'img_id=', 'policy=', 'core', 'image_boot_config'])
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300270 except getopt.GetoptError:
271 print(usage, file=sys.stderr)
272 sys.exit(Error.ARG)
273
274 for opt, arg in opts:
275 if opt in ('-h', '--help'):
276 print(usage, file=sys.stderr)
277 sys.exit()
278 elif opt in ('-p', '--platform'):
279 self.plat_id = arg
280 elif opt in ('-i', '--ifile'):
281 self.in_file = arg
282 elif opt in ('-o', '--ofile'):
283 self.out_file = arg
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300284 elif opt in ('-k', '--mk_file'):
285 self.mk_file = arg
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300286 elif opt in ('-a', '--fa_file'):
287 self.fa_file = arg
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300288 elif opt in ('-n', '--nonce_file'):
289 self.nonce_file = arg
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300290 elif opt in ('-d', '--img_id'):
291 self.img_id = arg
292 elif opt in ('-c', '--policy'):
293 self.policy = arg
294 elif opt in ('-m', '--core'):
295 self.set_core = True
296 elif opt in ('x', '--image_boot_config'):
297 self.image_boot_config = True
298
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300299 if any(len(x) == 0 for x in (self.in_file, self.out_file, self.mk_file, self.fa_file)):
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300300 print(usage, file=sys.stderr)
301 sys.exit(Error.ARG)
302
303
304class AreaList:
305 '''
306 A List of flash areas
307 ...
308
309 Attributes
310 ----------
311 plat : dict
312 Platform settings
313
314 flash : dict
315 External flash settings
316
317 use_overwrite : bool
318 Overwrite configuration in use
319
320 areas : list
321 Flash area parameter list
322
323 peers : set
324 Peers
325
326 trailers : set
327 Flash area trailers
328
329 internal_flash : bool
330 Internal flash in use
331
332 external_flash : bool
333 External flash in use
334
335 external_flash_xip : bool
336 External XIP in use
337
338 Methods
339 -------
340 get_min_erase_size:
341 Calculate minimum erase block size for int./ext. Flash
342
343 get_img_trailer_size:
344 Calculate image trailer size
345
346 process_int_area:
347 Process internal flash area
348
349 process_ext_area:
350 Process external flash area
351
352 chk_area:
353 Check area location (internal/external flash)
354
355 add_area:
356 Add flash area to AreaList.
357 Internal/external flash is detected by address.
358
359 generate_c_source:
360 Generate C source
361
362 create_flash_area_id:
363 Creates flash_area_id.h file.
364 '''
365
366 def __init__(self, plat, flash, use_overwrite):
367 self.plat = plat
368 self.flash = flash
369 self.use_overwrite = use_overwrite
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300370 self.use_direct_xip = False
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300371 self.areas = []
372 self.peers = {}
373 self.trailers = {}
374 self.internal_flash = False
375 self.external_flash = False
376 self.external_flash_xip = False
377
378 def get_min_erase_size(self):
379 '''Calculate minimum erase block size for int./ext. Flash '''
380 return self.plat['eraseSize'] if self.plat['flashSize'] > 0 \
381 else self.flash['eraseSize']
382
383 def get_img_trailer_size(self):
384 '''Calculate image trailer size'''
385 return self.get_min_erase_size()
386
387 def process_int_area(self, title, addr, fa_size,
388 img_trailer_size, shared_slot):
389 '''
390 Process internal flash area
391 Parameters:
392 ----------
393 title : str
394 Area name
395
396 addr : int
397 Area address
398
399 fa_size : int
400 Area size
401
402 img_trailer_size : int
403 Trailer size
404
405 shared_slot : bool
406 Shared slot option in use
407
408 Returns:
409 ----------
410 fa_device_id : str
411
412 fa_off : int
413
414 slot_sectors : int
415 '''
416 fa_device_id = 'FLASH_DEVICE_INTERNAL_FLASH'
417 fa_off = addr - self.plat['flashAddr']
418 if img_trailer_size is not None:
419 if self.use_overwrite:
420 if shared_slot:
421 print('Shared slot', title,
422 'is not supported in OVERWRITE mode',
423 file=sys.stderr)
424 sys.exit(Error.CONFIG_MISMATCH)
425 else:
426 # Check trailer alignment (start at the sector boundary)
427 align = (fa_off + fa_size - img_trailer_size) % \
428 self.plat['eraseSize']
429 if align != 0:
430 addr += self.plat['eraseSize'] - align
431 if addr + fa_size <= \
432 self.plat['flashAddr'] + self.plat['flashSize']:
433 print('Misaligned', title,
434 '- suggested address', hex(addr),
435 file=sys.stderr)
436 else:
437 print('Misaligned', title, file=sys.stderr)
438 sys.exit(Error.CONFIG_MISMATCH)
439 else:
440 # Check alignment (flash area should start at the sector boundary)
441 if fa_off % self.plat['eraseSize'] != 0:
442 print('Misaligned', title, file=sys.stderr)
443 sys.exit(Error.CONFIG_MISMATCH)
444 slot_sectors = int((fa_off % self.plat['eraseSize'] +
445 fa_size + self.plat['eraseSize'] - 1) //
446 self.plat['eraseSize'])
447 return fa_device_id, fa_off, slot_sectors
448
449 def process_ext_area(self, title, addr, fa_size,
450 img_trailer_size, shared_slot):
451 '''
452 Process external flash area
453 Parameters:
454 ----------
455 title : str
456 Area name
457
458 addr : int
459 Area address
460
461 fa_size : int
462 Area size
463
464 img_trailer_size : int
465 Trailer size
466
467 shared_slot : bool
468 Shared slot option in use
469
470 Returns:
471 ----------
472 fa_device_id : str
473
474 fa_off : int
475
476 slot_sectors : int
477 '''
478 if self.flash is None:
479 print('Unspecified SPI Flash IC',
480 file=sys.stderr)
481 sys.exit(Error.FLASH)
482 if addr + fa_size <= \
483 self.plat['smifAddr'] + self.flash['flashSize']:
484 flash_idx = 'CY_BOOT_EXTERNAL_DEVICE_INDEX'
485 fa_device_id = f'FLASH_DEVICE_EXTERNAL_FLASH({flash_idx})'
486 fa_off = addr - self.plat['smifAddr']
487 else:
488 print('Misfitting', title, file=sys.stderr)
489 sys.exit(Error.CONFIG_MISMATCH)
490 if img_trailer_size is not None:
491 if self.use_overwrite:
492 if shared_slot:
493 print('Shared slot', title,
494 'is not supported in OVERWRITE mode',
495 file=sys.stderr)
496 sys.exit(Error.CONFIG_MISMATCH)
497 else:
498 # Check trailer alignment (start at the sector boundary)
499 align = (fa_off + fa_size - img_trailer_size) % \
500 self.flash['eraseSize']
501 if align != 0:
502 peer_addr = self.peers.get(addr)
503 if shared_slot:
504 # Special case when using both int. and ext. memory
505 if self.plat['flashSize'] > 0 and \
506 align % self.plat['eraseSize'] == 0:
507 print('Note:', title, 'requires', align,
508 'padding bytes before trailer',
509 file=sys.stderr)
510 else:
511 print('Misaligned', title, file=sys.stderr)
512 sys.exit(Error.CONFIG_MISMATCH)
513 elif is_same_mem(addr, peer_addr) and \
514 addr % self.flash['eraseSize'] == \
515 peer_addr % self.flash['eraseSize']:
516 pass # postpone checking
517 else:
518 addr += self.flash['eraseSize'] - align
519 if addr + fa_size <= \
520 self.plat['smifAddr'] + self.flash['flashSize']:
521 print('Misaligned', title,
522 '- suggested address', hex(addr),
523 file=sys.stderr)
524 else:
525 print('Misaligned', title, file=sys.stderr)
526 sys.exit(Error.CONFIG_MISMATCH)
527 else:
528 # Check alignment (flash area should start at the sector boundary)
529 if fa_off % self.flash['eraseSize'] != 0:
530 print('Misaligned', title, file=sys.stderr)
531 sys.exit(Error.CONFIG_MISMATCH)
532 slot_sectors = int((fa_off % self.flash['eraseSize'] +
533 fa_size + self.flash['eraseSize'] - 1) //
534 self.flash['eraseSize'])
535 self.external_flash = True
536 if self.flash['XIP']:
537 self.external_flash_xip = True
538 return fa_device_id, fa_off, slot_sectors
539
540 def chk_area(self, addr, fa_size, peer_addr=None):
541 '''
542 Check area location (internal/external flash)
543 Parameters:
544 ----------
545 addr : int
546 Area address
547
548 fa_size : int
549 Area size
550
551 peer_addr : bool (optional)
552 Shared slot option in use
553
554 Returns:
555 ----------
556 None
557 '''
558 if peer_addr is not None:
559 self.peers[peer_addr] = addr
560 fa_limit = addr + fa_size
561 if self.plat['flashSize'] and \
562 addr >= self.plat['flashAddr'] and \
563 fa_limit <= self.plat['flashAddr'] + self.plat['flashSize']:
564 # Internal flash
565 self.internal_flash = True
566
567 def add_area(self, title,
568 fa_id, addr, fa_size,
569 img_trailer_size=None, shared_slot=False):
570 '''
571 Add flash area to AreaList.
572 Internal/external flash is detected by address.
573 Parameters:
574 ----------
575 title : str
576 Area name
577
578 fa_id : str
579 Area id
580
581 addr : int
582 Area address
583
584 fa_size : int
585 Area size
586
587 img_trailer_size : int
588 Trailer size (optional)
589
590 shared_slot : bool
591 Shared slot option in use (optional)
592
593 Returns:
594 ----------
595 slot_sectors : int
596 Number of sectors in a slot
597 '''
598 if fa_size == 0:
599 print('Empty', title, file=sys.stderr)
600 sys.exit(Error.CONFIG_MISMATCH)
601
602 fa_limit = addr + fa_size
603 if self.plat['flashSize'] and \
604 addr >= self.plat['flashAddr'] and \
605 fa_limit <= self.plat['flashAddr'] + self.plat['flashSize']:
606 # Internal flash
607 fa_device_id, fa_off, slot_sectors = self.process_int_area(
608 title, addr, fa_size, img_trailer_size, shared_slot)
609 align = self.plat['eraseSize']
610 elif self.plat['smifSize'] and \
611 addr >= self.plat['smifAddr'] and \
612 fa_limit <= self.plat['smifAddr'] + self.plat['smifSize']:
613 # External flash
614 fa_device_id, fa_off, slot_sectors = self.process_ext_area(
615 title, addr, fa_size, img_trailer_size, shared_slot)
616 align = self.flash['eraseSize']
617 else:
618 print('Invalid', title, file=sys.stderr)
619 sys.exit(Error.CONFIG_MISMATCH)
620
621 if shared_slot:
622 assert img_trailer_size is not None
623 tr_addr = addr + fa_size - img_trailer_size
624 tr_name = self.trailers.get(tr_addr)
625 if tr_name is not None:
626 print('Same trailer address for', title, 'and', tr_name,
627 file=sys.stderr)
628 sys.exit(Error.CONFIG_MISMATCH)
629 self.trailers[tr_addr] = title
630
631 # Ensure no flash areas on this device will overlap, except the
632 # shared slot
633 for area in self.areas:
634 if fa_device_id == area['fa_device_id']:
635 over = is_overlap(fa_off, fa_size,
636 area['fa_off'], area['fa_size'],
637 align)
638 if shared_slot and area['shared_slot']:
639 if not over: # images in shared slot should overlap
640 print(title, 'is not shared with', area['title'],
641 file=sys.stderr)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300642 elif over:
643 print(title, 'overlaps with', area['title'],
644 file=sys.stderr)
645 sys.exit(Error.CONFIG_MISMATCH)
646
647 self.areas.append({'title': title,
648 'shared_slot': shared_slot,
649 'fa_id': fa_id,
650 'fa_device_id': fa_device_id,
651 'fa_off': fa_off,
652 'fa_size': fa_size})
653 return slot_sectors
654
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200655 def generate_c_source(self, params, boot_and_upgrade):
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300656 '''
657 Generate C source
658 Parameters:
659 ----------
660 params : CmdLineParams
661 Application parameters
662
663 Returns:
664 ----------
665 None
666 '''
667 c_array = 'flash_areas'
668
669 try:
670 with open(params.out_file, "w", encoding='UTF-8') as out_f:
671
672 out_f.write(f'#include "{params.fa_file}"\n')
673 out_f.write(f'#include "flash_map_backend.h"\n\n')
674 out_f.write(f'#include "flash_map_backend_platform.h"\n\n')
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300675 out_f.write(f'const struct flash_area {c_array}[] = {{\n')
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300676 comma = len(self.areas)
677 area_count = 0
678 for area in self.areas:
679 comma -= 1
680 if area['fa_id'] is not None:
681 sss = ' /* Shared secondary slot */' \
682 if area['shared_slot'] else ''
683 out_f.writelines('\n'.join([
684 ' {' + sss,
685 f" .fa_id = {area['fa_id']},",
686 f" .fa_device_id = {area['fa_device_id']},",
687 f" .fa_off = {hex(area['fa_off'])}U,",
688 f" .fa_size = {hex(area['fa_size'])}U",
689 ' },' if comma else ' }', '']))
690 area_count += 1
691 out_f.write('};\n\n'
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300692 'const struct flash_area * const boot_area_descs[] = {\n')
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300693 for area_index in range(area_count):
694 out_f.write(f' &{c_array}[{area_index}U],\n')
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200695 out_f.write(' NULL\n};\n')
696
697 image_boot_mode = None
698
699 if params.image_boot_config:
700 image_boot_mode = process_boot_type(boot_and_upgrade)
701
702 if image_boot_mode:
703 out_f.write('\nimage_boot_config_t image_boot_config[BOOT_IMAGE_NUMBER] = {\n')
704 for mode in image_boot_mode:
705 out_f.writelines('\n'.join([
706 '\t{\n'
707 f"\t\t.mode = {mode['mode']},",
708 f"\t\t.address = {mode['address']},",
709 f"\t\t.size = {mode['size']},",
710 '\t},\n']))
711 out_f.write('};\n')
712
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300713 out_f.close()
714
715 except (FileNotFoundError, OSError):
716 print('Cannot create', params.out_file, file=sys.stderr)
717 sys.exit(Error.IO)
718
719 def create_flash_area_id(self, img_number, params):
720 """ Get 'img_number' and generate flash_area_id.h file' """
721
722 #check if params.fa_file already exists and it has FLASH_AREA_ID
723 if os.path.exists(params.fa_file):
724 with open(params.fa_file, "r", encoding='UTF-8') as fa_f:
725 content = fa_f.read()
726 res = content.find(f"FLASH_AREA_IMG_{img_number}_SECONDARY")
727 if res != -1:
728 fa_f.close()
729 return
730
731 fa_f.close()
732
733 try:
734 with open(params.fa_file, "w", encoding='UTF-8') as fa_f:
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200735 fa_f.write("#pragma once\n")
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300736 fa_f.write('/* AUTO-GENERATED FILE, DO NOT EDIT.'
737 ' ALL CHANGES WILL BE LOST! */\n')
738 fa_f.write(f'#include "flash_map_backend.h"\n\n')
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200739 fa_f.write('#include "bootutil/bootutil.h"\n')
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300740
INFINEON\DovhalA21babb72025-07-18 10:36:03 +0300741 fa_f.write(f'extern const struct flash_area {c_array}[];\n')
742 fa_f.write(f'extern const struct flash_area * const boot_area_descs[];\n')
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300743
744 #we always have BOOTLOADER and IMG_1_
745 fa_f.write("#define FLASH_AREA_BOOTLOADER ( 0u)\n\n")
746 fa_f.write("#define FLASH_AREA_IMG_1_PRIMARY ( 1u)\n")
747 fa_f.write("#define FLASH_AREA_IMG_1_SECONDARY ( 2u)\n\n")
748
749 fa_f.write("#define FLASH_AREA_IMAGE_SCRATCH ( 3u)\n")
750 fa_f.write("#define FLASH_AREA_IMAGE_SWAP_STATUS ( 7u)\n\n")
751
752 for img in range(2, img_number + 1):
753 """ img_id_primary and img_id_secondary must be aligned with the
754 flash_area_id, calculated in the functions
755 __STATIC_INLINE uint8_t FLASH_AREA_IMAGE_PRIMARY(uint32_t img_idx) and
756 __STATIC_INLINE uint8_t FLASH_AREA_IMAGE_SECONDARY(uint32_t img_idx),
757 in boot/cypress/platforms/memory/sysflash/sysflash.h
758 """
759
760 slots_for_image = 2
761 img_id_primary = None
762 img_id_secondary = None
763
764 if img == 2:
765 img_id_primary = int(slots_for_image * img)
766 img_id_secondary = int(slots_for_image * img + 1)
767
768 #number 7 is used for FLASH_AREA_IMAGE_SWAP_STATUS, so the next is 8
769 if img >= 3:
770 img_id_primary = int(slots_for_image * img + 2)
771 img_id_secondary = int(slots_for_image * img + 3)
772
773 fa_f.write(f"#define FLASH_AREA_IMG_{img}_PRIMARY ( {img_id_primary}u)\n")
774 fa_f.write(f"#define FLASH_AREA_IMG_{img}_SECONDARY ( {img_id_secondary}u)\n\n")
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200775
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300776 if self.plat.get('bitsPerCnt'):
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300777 list_counters = process_policy_20829(params.policy)
778 if list_counters is not None:
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200779 form_max_counter_array(list_counters, fa_f)
780
781 fa_f.writelines('\n'.join([
782 '',
783 'typedef enum',
784 '{',
785 '\tIMAGE_BOOT_MODE_FLASH = 0U,',
786 '\tIMAGE_BOOT_MODE_RAM = 1U,',
787 '} image_boot_mode_t;',
788 '',
789 'typedef struct image_boot_config_s {',
790 '\timage_boot_mode_t mode;',
791 '\tuint32_t address;',
792 '\tuint32_t size;',
793 '} image_boot_config_t;',
794 '',
795 'extern image_boot_config_t image_boot_config[BOOT_IMAGE_NUMBER];'
796 ]))
797
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300798 fa_f.close()
799
800 except (FileNotFoundError, OSError):
801 print('\nERROR: Cannot create ', params.fa_file, file=sys.stderr)
802 sys.exit(Error.IO)
803
804
805def cvt_dec_or_hex(val, desc):
806 """Convert (hexa)decimal string to number"""
807 try:
808 return int(val, 0)
809 except ValueError:
810 print('Invalid value', val, 'for', desc, file=sys.stderr)
811 sys.exit(Error.VALUE)
812
813
814def get_val(obj, attr):
815 """Get JSON 'value'"""
816 obj = obj[attr]
817 try:
818 return cvt_dec_or_hex(obj['value'], obj['description'])
819 except KeyError as key:
820 print('Malformed JSON:', key,
821 'is missing in', "'" + attr + "'",
822 file=sys.stderr)
823 sys.exit(Error.JSON)
824
825
826def get_bool(obj, attr, def_val=False):
827 """Get JSON boolean value (returns def_val if it is missing)"""
828 ret_val = def_val
829 obj = obj.get(attr)
830 if obj is not None:
831 try:
832 val = str(obj['value']).lower()
833 desc = obj['description']
834 if val == 'true':
835 ret_val = True
836 elif val == 'false':
837 ret_val = False
838 else:
839 print('Invalid value', val, 'for', desc, file=sys.stderr)
840 sys.exit(Error.VALUE)
841 except KeyError as key:
842 print('Malformed JSON:', key,
843 'is missing in', "'" + attr + "'",
844 file=sys.stderr)
845 sys.exit(Error.JSON)
846 return ret_val
847
848
849def get_str(obj, attr, def_val=None):
850 """Get JSON string value (returns def_val if it is missing)"""
851 ret_val = def_val
852 obj = obj.get(attr)
853 if obj is not None:
854 try:
855 ret_val = str(obj['value'])
856 except KeyError as key:
857 print('Malformed JSON:', key,
858 'is missing in', "'" + attr + "'",
859 file=sys.stderr)
860 sys.exit(Error.JSON)
861 return ret_val
862
863
864class AddrSize:
865 """Bootloader area"""
866
867 def __init__(self, bootloader, addr_name, size_name):
868 self.addr = get_val(bootloader, addr_name)
869 self.size = get_val(bootloader, size_name)
870
871
872def calc_status_size(boot_swap_status_row_sz, max_img_sectors,
873 img_number, scratch_flag=True):
874 """Estimate status size, see swap_status.h"""
875 boot_swap_status_cnt_sz = 4
876 boot_swap_status_crc_sz = 4
877 boot_swap_status_mgcrec_sz = 4
878 boot_swap_status_trailer_size = 64
879 boot_swap_status_payld_sz = \
880 boot_swap_status_row_sz - boot_swap_status_mgcrec_sz - \
881 boot_swap_status_cnt_sz - boot_swap_status_crc_sz
882 boot_swap_status_sect_rows_num = \
883 int((max_img_sectors - 1) //
884 boot_swap_status_payld_sz) + 1
885 boot_swap_status_trail_rows_num = \
886 int((boot_swap_status_trailer_size - 1) //
887 boot_swap_status_payld_sz) + 1
888 boot_swap_status_d_size = \
889 boot_swap_status_row_sz * \
890 (boot_swap_status_sect_rows_num + boot_swap_status_trail_rows_num)
891 boot_swap_status_mult = 2
892 boot_swap_status_size = boot_swap_status_mult * boot_swap_status_d_size
893 status_zone_cnt = 2 * img_number
894 if scratch_flag:
895 status_zone_cnt += 1
896 return boot_swap_status_size * status_zone_cnt
897
898
899def process_json(in_file):
900 """Process JSON"""
901 try:
902 with open(in_file, encoding='UTF-8') as in_f:
903 try:
904 flash_map = json.load(in_f)
905 except ValueError:
906 print('Cannot parse', in_file, file=sys.stderr)
907 sys.exit(Error.IO)
908 except (FileNotFoundError, OSError):
909 print('Cannot open', in_file, file=sys.stderr)
910 sys.exit(Error.IO)
911 flash = flash_map.get('external_flash')
912 if flash is not None:
913 flash = flash[0]
914 model = flash.get('model')
915 mode = flash.get('mode')
916 if model is not None:
917 try:
918 flash = flashDict[model]
919 except KeyError:
920 print('Supported SPI Flash ICs are:',
921 ', '.join(flashDict.keys()),
922 file=sys.stderr)
923 sys.exit(Error.FLASH)
924 else:
925 try:
926 flash = {'flashSize': cvt_dec_or_hex(flash['flash-size'],
927 'flash-size'),
928 'eraseSize': cvt_dec_or_hex(flash['erase-size'],
929 'erase-size')}
930 except KeyError as key:
931 print('Malformed JSON:', key,
932 "is missing in 'external_flash'",
933 file=sys.stderr)
934 sys.exit(Error.FLASH)
935 flash.update({'XIP': str(mode).upper() == 'XIP'})
936 return flash_map.get('boot_and_upgrade', None), flash_map.get('ram_app_staging', None), flash
937
938def process_boot_type(boot_and_upgrade):
939 image_boot_mode = []
940
941 for app_index in range(1, 5):
942 app_ident = f'application_{app_index}'
943 application = boot_and_upgrade.get(app_ident)
944
945 if application:
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200946 mem = application.get('ram_boot')
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300947
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200948 if mem:
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300949 image_boot_mode.append(
950 {
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200951 'mode': 'IMAGE_BOOT_MODE_RAM',
952 'address': mem.get('address', {}).get('value', 0),
953 'size': mem.get('size', {}).get('value', 0),
954 }
955 )
956 else :
957 mem = application.get('flash')
958 image_boot_mode.append(
959 {
960 'mode': 'IMAGE_BOOT_MODE_FLASH',
961 'address': mem.get('address', {}).get('value', 0),
962 'size': mem.get('size', {}).get('value', 0),
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300963 }
964 )
965
966 return image_boot_mode
967
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300968def process_images(area_list, boot_and_upgrade):
969 """Process images"""
970 app_count = 0
971 slot_sectors_max = 0
972 all_shared = get_bool(boot_and_upgrade['bootloader'], 'shared_slot')
973 any_shared = all_shared
974 app_core = None
975 apps_flash_map = [None, ]
976 apps_ram_map = [None, ]
977
978 for stage in range(2):
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200979 for app_index in range(1, MAX_IMAGE_NUMBERS):
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300980
981 app_flash_map = {}
982 app_ram_map = {}
983 app_ram_boot = False
984
985 try:
986 app_ident = f'application_{app_index}'
987 application = boot_and_upgrade[app_ident]
988
989 try:
990 flash = application['flash']
991 except KeyError:
992 #Backward compatibility
993 flash = application
994
995 try:
996 config = application['config']
997 except KeyError:
998 #Backward compatibility
999 config = application
1000
1001 try:
1002 ram = application['ram']
1003 except KeyError:
1004 try:
1005 ram = application['ram_boot']
1006 app_ram_boot = True
1007 except KeyError:
1008 ram = None
1009
1010 try:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001011 try:
1012 primary_addr = get_val(flash, 'primary_slot')
1013 secondary_addr = get_val(flash, 'secondary_slot')
1014 primary_size = get_val(flash, 'primary_slot_size')
1015 secondary_size = get_val(flash, 'secondary_slot_size')
1016
1017 area_list.use_direct_xip = True
1018
1019 except KeyError:
1020 primary_addr = get_val(flash, 'address')
1021 secondary_addr = get_val(flash, 'upgrade_address')
1022 primary_size = get_val(flash, 'size')
1023 secondary_size = get_val(flash, 'upgrade_size')
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001024
1025 if ram is not None:
1026 app_ram_addr = get_val(ram, 'address')
1027 app_ram_size = get_val(ram, 'size')
1028 else:
1029 app_ram_addr = None
1030 app_ram_size = None
1031
1032 except KeyError as key:
1033 print('Malformed JSON:', key, 'is missing',
1034 file=sys.stderr)
1035 sys.exit(Error.JSON)
1036 if stage == 0:
1037 if primary_size != secondary_size:
1038 print('Primary and secondary slot sizes'
1039 ' are different for', app_ident,
1040 file=sys.stderr)
1041 sys.exit(Error.VALUE)
1042 area_list.chk_area(primary_addr, primary_size)
1043 area_list.chk_area(secondary_addr, secondary_size,
1044 primary_addr)
1045 if config is None or config.get('core') is None:
1046 if app_index == 1:
1047 app_core = area_list.plat['appCore']
1048 elif app_index > 1:
1049 print('"core" makes sense only for the 1st app',
1050 file=sys.stderr)
1051 sys.exit(Error.VALUE)
1052 else:
1053 app_core = get_str(config, 'core',
1054 area_list.plat['appCore'])
1055 if app_index == 1:
1056 app_core = area_list.plat['allCores'].get(app_core.lower())
1057 if app_core is None:
1058 print('Unknown "core"', file=sys.stderr)
1059 sys.exit(Error.VALUE)
1060 else:
1061 slot_sectors_max = max(
1062 slot_sectors_max,
1063 area_list.add_area(
1064 f'{app_ident} (primary slot)',
1065 f'FLASH_AREA_IMG_{app_index}_PRIMARY',
1066 primary_addr, primary_size,
1067 area_list.get_img_trailer_size()))
1068 shared_slot = get_bool(flash, 'shared_slot', all_shared)
1069 any_shared = any_shared or shared_slot
1070 slot_sectors_max = max(
1071 slot_sectors_max,
1072 area_list.add_area(
1073 f'{app_ident} (secondary slot)',
1074 f'FLASH_AREA_IMG_{app_index}_SECONDARY',
1075 secondary_addr, secondary_size,
1076 area_list.get_img_trailer_size(),
1077 shared_slot))
1078
1079 app_slot_prim = {"address": hex(primary_addr), "size": hex(primary_size)}
1080 app_slot_sec = {"address": hex(secondary_addr), "size": hex(secondary_size)}
1081
1082 app_flash_map.update({"primary": app_slot_prim, "secondary": app_slot_sec})
1083 apps_flash_map.append(app_flash_map)
1084
1085 if ram is not None:
1086 app_ram_map.update({"address": app_ram_addr
1087 , "size": app_ram_size
1088 , "ram_boot": app_ram_boot})
1089 apps_ram_map.append(app_ram_map)
1090 else:
1091 apps_ram_map = None
1092
1093 app_count = app_index
1094
1095 except KeyError:
1096 break
1097 if app_count == 0:
1098 print('Malformed JSON: no application(s) found',
1099 file=sys.stderr)
1100 sys.exit(Error.JSON)
1101
1102 return app_core, app_count, slot_sectors_max, apps_flash_map, apps_ram_map, any_shared
1103
1104
1105def process_policy_20829(in_policy):
1106 """Process policy file to get data of NV-counter"""
1107 list_counters = None
1108
1109 try:
1110 with open(in_policy, encoding='UTF-8') as in_f:
1111 try:
1112 policy = json.load(in_f)
1113 except ValueError:
1114 print('\nERROR: Cannot parse', in_policy,'\n', file=sys.stderr)
1115 sys.exit(Error.IO)
1116 finally:
1117 in_f.close()
1118 except (FileNotFoundError, OSError):
1119 print('Cannot open', in_policy, file=sys.stderr)
1120 sys.exit(Error.IO)
1121
1122 try:
1123 nv_cnt = policy["device_policy"]['reprovisioning']['nv_counter']
1124 list_values = nv_cnt["value"]
1125 list_counters = nv_cnt["bits_per_cnt"]
1126 except KeyError:
1127 print("\nERROR: Check path to 'nv_counter' and its correctness in policy file", in_policy,
1128 ".\n", file=sys.stderr)
1129 sys.exit(Error.POLICY)
1130
1131 #Check correctness of NV-counter
1132 try:
1133 len_list_value = len(list_values)
1134 len_list_counters = len(list_counters)
1135 except TypeError:
1136 print("\nERROR: Fields 'value' and 'bits_per_cnt' of 'nv_counter' in policy file",
1137 in_policy,"must be arrays.\n", file=sys.stderr)
1138 sys.exit(Error.POLICY)
1139
1140 if len_list_value != len_list_counters:
1141 print("\nERROR: Fields 'value' and 'bits_per_cnt' of 'nv_counter' in policy file",
1142 in_policy,"must have the same size.\n", file=sys.stderr)
1143 sys.exit(Error.POLICY)
1144
1145 sum_all_counters = 0
1146 for i in range(len_list_value):
1147 sum_all_counters += list_counters[i]
1148 if list_values[i] > list_counters[i]:
1149 print("\nERROR: Field 'value' cannot be more then 'bits_per_cnt'.", file=sys.stderr)
1150 print("Check 'nv_counter' in policy file", in_policy,"\n", file=sys.stderr)
1151 sys.exit(Error.POLICY)
1152
1153 sum_all_bit_nv_counter = 32
1154 if sum_all_counters != sum_all_bit_nv_counter:
1155 print("\nERROR: The sum of all 'bits_per_cnt' must be equal to 32.", file=sys.stderr)
1156 print("Check 'nv_counter' in policy file", in_policy,"\n", file=sys.stderr)
1157 sys.exit(Error.POLICY)
1158
1159 return list_counters
1160
1161
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +02001162def form_max_counter_array(in_list, fa_f):
1163 '''Write bit_per_count array to output file '''
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001164 #ifdef here is needed to fix Rule 12.2 MISRA violation
1165 out_array_str = "\n#ifdef NEED_MAX_COUNTERS\nstatic const uint8_t bits_per_cnt[] = {"
1166
1167 #in_list is checked in prior function 'process_policy()'
1168 for i, list_member in enumerate(in_list):
1169 out_array_str += str(list_member)
1170 if i < len(in_list) - 1:
1171 out_array_str += ", "
1172 out_array_str += "};\n#endif\n"
1173
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +02001174 fa_f.write(out_array_str)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001175
1176
1177def main():
1178 """Flash map converter"""
1179 params = CmdLineParams()
1180
1181 try:
1182 plat = platDict[params.plat_id]
1183 except KeyError:
1184 print('Supported platforms are:', ', '.join(platDict.keys()),
1185 file=sys.stderr)
1186 sys.exit(Error.POLICY)
1187
1188 try:
1189 boot_and_upgrade, ram_app_staging, flash = process_json(params.in_file)
1190 bootloader = boot_and_upgrade['bootloader']
1191 try:
1192 bootloader_config = bootloader['config']
1193 except KeyError:
1194 #Backward compatibility
1195 bootloader_config = bootloader
1196
1197 if ram_app_staging is not None:
1198 try:
1199 ram_app_staging_size = get_val(ram_app_staging, 'size')
1200 ram_app_staging_ext_mem_addr = get_val(ram_app_staging, 'external_mememory_address')
1201 ram_app_staging_sram_stage_addr = get_val(ram_app_staging, 'sram_stage_address')
1202 ram_app_staging_reset_trigger = get_bool(ram_app_staging, 'reset_after_staging')
1203 except KeyError:
1204 ram_app_staging = None
1205
1206 try:
1207 bootloader_flash = bootloader['flash']
1208 except KeyError:
1209 #Backward compatibility
1210 bootloader_flash = bootloader
1211
1212 try:
1213 bootloader_ram = bootloader['ram']
1214 except KeyError:
1215 #Backward compatibility
1216 bootloader_ram = bootloader
1217
1218 boot_flash_area = AddrSize(bootloader_flash, 'address', 'size')
1219 boot_ram_area = AddrSize(bootloader_ram, 'address', 'size')
1220 except KeyError as key:
1221 print('Malformed JSON:', key, 'is missing',
1222 file=sys.stderr)
1223 sys.exit(Error.JSON)
1224
1225 try:
1226 scratch = AddrSize(bootloader_flash, 'scratch_address', 'scratch_size')
1227 except KeyError:
1228 scratch = None
1229
1230 try:
1231 swap_status = AddrSize(bootloader_flash, 'status_address', 'status_size')
1232 except KeyError:
1233 swap_status = None
1234
1235 try:
1236 bootloader_shared_data = bootloader['shared_data']
1237 boot_shared_data_area = AddrSize(bootloader_shared_data, 'address', 'size')
1238 except KeyError:
1239 boot_shared_data_area = None
1240
1241 try:
1242 bootloader_startup = get_bool(bootloader_config, 'startup')
1243 except KeyError:
1244 bootloader_startup = None
1245
1246 try:
1247 ram_app_area = AddrSize(bootloader_config['ram_boot'], 'address', 'size')
1248 except KeyError:
1249 ram_app_area = None
1250
1251
1252 # Create flash areas
1253 area_list = AreaList(plat, flash, scratch is None and swap_status is None)
1254 area_list.add_area('bootloader', 'FLASH_AREA_BOOTLOADER',
1255 boot_flash_area.addr, boot_flash_area.size)
1256
1257 # Service RAM app (optional)
1258 service_app = boot_and_upgrade.get('service_app')
1259 app_binary = None
1260 input_params = None
1261 app_desc = None
1262 if service_app is not None:
1263 if plat['flashSize'] > 0:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001264 print('service_app is unsupported on this platform', file=sys.stderr)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001265 sys.exit(Error.CONFIG_MISMATCH)
1266 try:
1267 app_binary = AddrSize(service_app, 'address', 'size')
1268 input_params = AddrSize(service_app, 'params_address', 'params_size')
1269 app_desc = AddrSize(service_app, 'desc_address', 'desc_size')
1270 if input_params.addr != app_binary.addr + app_binary.size or \
1271 app_desc.addr != input_params.addr + input_params.size or \
1272 app_desc.size != SERVICE_APP_SZ:
1273 print('Malformed service_app definition', file=sys.stderr)
1274 sys.exit(Error.CONFIG_MISMATCH)
1275 area_list.add_area('service_app', None, app_binary.addr,
1276 app_binary.size + input_params.size + app_desc.size)
1277 except KeyError as key:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001278 print('Malformed JSON:', key, 'is missing', file=sys.stderr)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001279 sys.exit(Error.JSON)
1280
1281 # Fill flash areas
1282 app_core, app_count, slot_sectors_max, apps_flash_map, apps_ram_map, shared_slot = \
1283 process_images(area_list, boot_and_upgrade)
1284
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001285 cy_img_hdr_size = 0x400
1286 app_start = int(apps_flash_map[1].get("primary").get("address"), 0) + cy_img_hdr_size
1287
1288 if app_start % plat['VTAlign'] != 0:
1289 print('Starting address', apps_flash_map[1].get("primary").get("address"),
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001290 '+', hex(cy_img_hdr_size), 'must be aligned to', hex(plat['VTAlign']),
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001291 file=sys.stderr)
1292 sys.exit(Error.CONFIG_MISMATCH)
1293
1294 slot_sectors_max = max(slot_sectors_max, 32)
1295
1296 if swap_status is not None:
1297 status_size_min = calc_status_size(area_list.get_min_erase_size(),
1298 slot_sectors_max,
1299 app_count,
1300 scratch is not None)
1301
1302 if swap_status.size < status_size_min:
1303 print('Insufficient swap status area - suggested size',
1304 hex(status_size_min),
1305 file=sys.stderr)
1306 sys.exit(Error.CONFIG_MISMATCH)
1307 area_list.add_area('swap status partition',
1308 'FLASH_AREA_IMAGE_SWAP_STATUS',
1309 swap_status.addr, swap_status.size)
1310
1311 if scratch is not None:
1312 area_list.add_area('scratch area',
1313 'FLASH_AREA_IMAGE_SCRATCH',
1314 scratch.addr, scratch.size)
1315
1316 # Compare size 'bit_per_cnt' and number of images.
1317 # 'service_app' is used only when HW rollback counter exists
1318 if plat.get('bitsPerCnt') is not None and service_app is not None:
1319 plat['bitsPerCnt'] = True
1320 list_counters = process_policy_20829(params.policy)
1321 if list_counters is not None and len(list_counters) != app_count:
1322 print("\nERROR: 'bits_per_cnt' must be present for each image!",
1323 file=sys.stderr)
1324 print("Please, check secure provisioning and reprovisioning policies.\n",
1325 file=sys.stderr)
1326 sys.exit(Error.CONFIG_MISMATCH)
1327
1328 # Image id parameter is not used for MCUBootApp
1329 if params.img_id is None:
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +02001330 area_list.generate_c_source(params, boot_and_upgrade)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001331
1332 area_list.create_flash_area_id(app_count, params)
1333
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001334 # Write necessary values to makefile
1335 try:
1336 mk_file = open(params.mk_file, "w")
1337 except (FileNotFoundError, OSError):
1338 print('\nERROR: Cannot create ', params.mk_file, file=sys.stderr)
1339 sys.exit(Error.IO)
1340
1341 print('# AUTO-GENERATED FILE, DO NOT EDIT. ALL CHANGES WILL BE LOST!', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001342 if params.set_core:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001343 print('CORE :=', plat['allCores'][plat['bootCore'].lower()], file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001344
1345 if ram_app_staging is not None:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001346 print('USE_STAGE_RAM_APPS := 1', file=mk_file)
1347 print('RAM_APP_STAGING_EXT_MEM_ADDR := ', hex(ram_app_staging_ext_mem_addr), file=mk_file)
1348 print('RAM_APP_STAGING_SRAM_MEM_ADDR :=', hex(ram_app_staging_sram_stage_addr), file=mk_file)
1349 print('RAM_APP_STAGING_SIZE := ', hex(ram_app_staging_size), file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001350 if ram_app_staging_reset_trigger is True:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001351 print('RAM_APP_RESET_TRIGGER := 1', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001352
1353 if bootloader_startup is True:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001354 print('BOOTLOADER_STARTUP := 1', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001355
1356 if ram_app_area is not None:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001357 print('USE_MCUBOOT_RAM_LOAD := 1', file=mk_file)
1358 print('IMAGE_EXECUTABLE_RAM_START :=', hex(ram_app_area.addr), file=mk_file)
1359 print('IMAGE_EXECUTABLE_RAM_SIZE :=', hex(ram_app_area.size), file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001360
1361 if boot_shared_data_area is not None:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001362 print('USE_MEASURED_BOOT := 1', file=mk_file)
1363 print('USE_DATA_SHARING := 1', file=mk_file)
1364 print('BOOT_SHARED_DATA_ADDRESS :=', hex(boot_shared_data_area.addr)+'U', file=mk_file)
1365 print('BOOT_SHARED_DATA_SIZE :=', hex(boot_shared_data_area.size)+'U', file=mk_file)
1366 print('BOOT_SHARED_DATA_RECORD_SIZE :=', hex(boot_shared_data_area.size)+'U', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001367
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001368 print('BOOTLOADER_ORIGIN :=', hex(boot_flash_area.addr), file=mk_file)
1369 print('BOOTLOADER_SIZE :=', hex(boot_flash_area.size), file=mk_file)
1370 print('BOOTLOADER_RAM_ORIGIN :=', hex(boot_ram_area.addr), file=mk_file)
1371 print('BOOTLOADER_RAM_SIZE :=', hex(boot_ram_area.size), file=mk_file)
1372 print('APP_CORE :=', app_core, file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001373
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +02001374 # for blinky
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001375 if params.img_id is not None:
1376 primary_img_start = apps_flash_map[int(params.img_id)].get("primary").get("address")
1377 secondary_img_start = apps_flash_map[int(params.img_id)].get("secondary").get("address")
1378 slot_size = apps_flash_map[int(params.img_id)].get("primary").get("size")
1379
1380 if apps_ram_map:
1381 image_ram_address = apps_ram_map[int(params.img_id)].get("address")
1382 image_ram_size = apps_ram_map[int(params.img_id)].get("size")
1383 image_ram_boot = apps_ram_map[int(params.img_id)].get("ram_boot")
1384 if image_ram_address and image_ram_size:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001385 print('IMG_RAM_ORIGIN := ' + hex(image_ram_address), file=mk_file)
1386 print('IMG_RAM_SIZE := ' + hex(image_ram_size), file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001387 if image_ram_boot is True:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001388 print('USE_MCUBOOT_RAM_LOAD := 1', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001389
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001390 print('PRIMARY_IMG_START := ' + primary_img_start, file=mk_file)
1391 print('SECONDARY_IMG_START := ' + secondary_img_start, file=mk_file)
1392 print('SLOT_SIZE := ' + slot_size, file=mk_file)
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +02001393 # for bootloader
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001394 else:
1395 if apps_ram_map:
1396 ram_load_counter = 0
1397 for img in apps_ram_map:
1398 if img is not None and img.get("ram_boot"):
1399 ram_load_counter += 1
1400
1401 if ram_load_counter != 0:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001402 print('USE_MCUBOOT_RAM_LOAD := 1', file=mk_file)
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +02001403 if ram_load_counter == 1:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001404 print(f'IMAGE_EXECUTABLE_RAM_START := {hex(apps_ram_map[1].get("address"))}', file=mk_file)
1405 print(f'IMAGE_EXECUTABLE_RAM_SIZE := {hex(apps_ram_map[1].get("size"))}', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001406 else:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001407 print('USE_MCUBOOT_MULTI_MEMORY_LOAD := 1', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001408
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001409 print('MAX_IMG_SECTORS :=', slot_sectors_max, file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001410
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001411 print('MCUBOOT_IMAGE_NUMBER :=', app_count, file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001412 if area_list.external_flash:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001413 print('USE_EXTERNAL_FLASH := 1', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001414 if area_list.external_flash_xip:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001415 print('USE_XIP := 1', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001416
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001417 if area_list.use_direct_xip:
1418 print('USE_DIRECT_XIP := 1', file=mk_file)
1419 elif area_list.use_overwrite:
1420 print('USE_OVERWRITE := 1', file=mk_file)
1421
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001422 if shared_slot:
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001423 print('USE_SHARED_SLOT := 1', file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001424 if service_app is not None:
1425 print('PLATFORM_SERVICE_APP_OFFSET :=',
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001426 hex(app_binary.addr - plat['smifAddr']), file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001427 print('PLATFORM_SERVICE_APP_INPUT_PARAMS_OFFSET :=',
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001428 hex(input_params.addr - plat['smifAddr']), file=mk_file)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001429 print('PLATFORM_SERVICE_APP_DESC_OFFSET :=',
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001430 hex(app_desc.addr - plat['smifAddr']), file=mk_file)
1431 print('USE_HW_ROLLBACK_PROT := 1', file=mk_file)
1432 mk_file.close()
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001433
INFINEON\DovhalA21babb72025-07-18 10:36:03 +03001434 if params.nonce_file and not os.path.isfile(params.nonce_file):
1435 try:
1436 with open(params.policy, encoding='UTF-8') as in_f:
1437 try:
1438 policy = json.load(in_f)
1439 except ValueError:
1440 print('\nERROR: Cannot parse', params.policy, '\n', file=sys.stderr)
1441 sys.exit(Error.IO)
1442 except (FileNotFoundError, OSError):
1443 print('\nERROR: Cannot open', params.policy, file=sys.stderr)
1444 sys.exit(Error.IO)
1445
1446 policy_key_path = policy.get('pre_build', {}).get('keys', {}).get('encrypt_key', {}).get('value', {})
1447 if policy_key_path:
1448 full_key_path = os.path.join(os.path.dirname(params.policy), policy_key_path)
1449 try:
1450 with open(full_key_path, "rb") as aes_key_file:
1451 aes_key = aes_key_file.read()
1452 except (FileNotFoundError, OSError):
1453 print('Missing encryption key', full_key_path, '\nNonce file generation skipped')
1454 sys.exit(0)
1455
1456 if len(aes_key) != AES_BLOCK_SIZE:
1457 print('\nERROR: Wrong AES key size in ', full_key_path, file=sys.stderr)
1458 sys.exit(Error.CONFIG_MISMATCH)
1459
1460 for i in range(NONCE_GEN_RETRIES):
1461 random_bytes = os.urandom(NONCE_RAND_SIZE)
1462 fa_ids = [entry['fa_off'] + entry['fa_size'] for entry in area_list.areas
1463 if entry['fa_id'].startswith('FLASH_AREA_IMG_')]
1464 for fa_end_addr in fa_ids:
1465 img_ok_addr = SHAB_ADDR + fa_end_addr - IMG_OK_OFFSET
1466 img_ok_block_addr = img_ok_addr & ~(AES_BLOCK_SIZE - 1)
1467 nonce = img_ok_block_addr.to_bytes(AES_BLOCK_SIZE - NONCE_RAND_SIZE,
1468 byteorder='little') + random_bytes
1469 cipher = Cipher(algorithms.AES(aes_key), modes.CTR(nonce))
1470 encryptor_0 = cipher.encryptor()
1471 encryptor_1 = cipher.encryptor()
1472 encrypted_block_0 = encryptor_0.update(b'\x00' * AES_BLOCK_SIZE)
1473 encrypted_block_1 = encryptor_1.update(b'\xFF' * AES_BLOCK_SIZE)
1474
1475 if encrypted_block_0[img_ok_addr - img_ok_block_addr] == IMG_OK_VALUE or \
1476 encrypted_block_1[img_ok_addr - img_ok_block_addr] == IMG_OK_VALUE:
1477 break
1478 else:
1479 break
1480 else:
1481 print("\nERROR: Can't generate valid NONCE sequence", file=sys.stderr)
1482 sys.exit(Error.VALUE)
1483
1484 try:
1485 if '/' in params.nonce_file or '\\' in params.nonce_file:
1486 os.makedirs(os.path.dirname(params.nonce_file), exist_ok=True)
1487 with open(params.nonce_file, "wb") as nonce_f:
1488 nonce_f.write(random_bytes)
1489 except (FileNotFoundError, OSError):
1490 print('\nERROR: Cannot create ', params.nonce_file, file=sys.stderr)
1491 sys.exit(Error.IO)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001492
1493if __name__ == '__main__':
1494 main()